diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 58e036c..d013ada 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -66,7 +66,7 @@ Functions are procedural and single-call, not pipeline-aware. Skip the blocks un ### Global state `Initialize-*` functions populate `$global:OSDCloud*` variables; `Invoke-*` and `Get-*` functions read them. -Key globals: `$global:OSDCloudDevice`, `$global:OSDCloudDeploy`, `$global:OSDCloudWorkflowTasks`, `$global:OSDCloudWorkflowInvoke`, `$global:Architecture`, `$global:IsWinPE`, `$global:IsVM`, `$global:IsOnBattery`. +Key globals: `$global:OSDCoreDevice`, `$global:OSDCloudDeploy`, `$global:OSDCloudWorkflowTasks`, `$global:OSDCloudWorkflowInvoke`, `$global:Architecture`, `$global:IsWinPE`, `$global:IsVM`, `$global:IsOnBattery`. Document any new global variables added. ### Naming diff --git a/.github/instructions/osdcloud-catalog-update.instructions.md b/.github/instructions/osdcloud-catalog-update.instructions.md index fe49400..cf59bd7 100644 --- a/.github/instructions/osdcloud-catalog-update.instructions.md +++ b/.github/instructions/osdcloud-catalog-update.instructions.md @@ -1,14 +1,14 @@ --- -description: "Use when adding a new Windows OS build to catalogs/operatingsystem/, updating driver pack snapshots in core/driverpacks/, adding a new Surface model to surface.json, or adding a new Panasonic model to panasonic.json. Covers file naming, XML/JSON schemas, the build-number switch statement, and required workflow config changes." +description: "Use when adding a new Windows OS build to OSDCloud/core/operatingsystems/, updating driver pack snapshots in OSDCloud/core/driverpacks/, adding a new Surface model to OSDCloud/core/driverpacks/surface.json, or adding a new Panasonic model to OSDCloud/core/driverpacks/panasonic.json. Covers file naming, XML/JSON schemas, the build-number switch statement, and required workflow config changes." --- # Catalog Update Guidelines -All catalog files live in `catalogs/`. They are committed snapshots used as offline fallbacks or as the primary data source. Do not edit them for content that is fetched dynamically at runtime from OEM sources. +Catalog snapshots live under the repository's OSDCloud/core tree. OS metadata is stored in `OSDCloud/core/operatingsystems/`, and driver-pack snapshots live in `OSDCloud/core/driverpacks/`. These files are committed fallbacks and should not be edited for content that is fetched dynamically at runtime from OEM sources. --- -## OS catalogs (`catalogs/operatingsystem/`) +## OS catalogs (`OSDCloud/core/operatingsystems/`) ### File naming @@ -51,9 +51,9 @@ Each `` element contains: ### Adding a new OS build — checklist -1. **Add the XML file** to `catalogs/operatingsystem/` following the naming convention above. +1. **Add the XML file** to `OSDCloud/core/operatingsystems/` following the naming convention above. -2. **Register the build number** in `private/Get-OSDCloudCoreOperatingSystems.ps1`. +2. **Register the build number** in `OSDCloud/private/core/Get-OSDCloudCoreOperatingSystems.ps1`. Locate the `switch ($OSBuild)` block and add a new case: ```powershell @@ -72,7 +72,7 @@ Each `` element contains: Without this entry, the build's ESD entries are silently skipped. -3. **Update workflow OS configs** — for every channel that should offer the new build, add the version string to `os-amd64.json` and `os-arm64.json`: +3. **Update workflow OS configs** — for every channel that should offer the new build, add the version string to `OSDCloud/workflow//os-amd64.json` and `OSDCloud/workflow//os-arm64.json`: ```json { @@ -90,27 +90,27 @@ Each `` element contains: --- -## Driver pack catalogs (`core/driverpacks/`) +## Driver pack catalogs (`OSDCloud/core/driverpacks/`) ### Overview of catalog files | File | Format | Manufacturer | Updated by | |---|---|---|---| -| `dell.xml` | OEM XML snapshot | Dell | Download + replace | -| `hp.xml` | OEM XML snapshot | HP | Download + replace | -| `lenovo.xml` | OEM XML snapshot | Lenovo | Download + replace | -| `surface.json` | JSON array | Microsoft (Surface) | Manual edit / `Update-MicrosoftCatalog.ps1` | -| `panasonic.json` | Nested JSON | Panasonic | Manual edit | -| `generic.json` | JSON array | ARM64 / multi-OEM | Manual edit | +| `OSDCloud/core/driverpacks/dell.xml` | OEM XML snapshot | Dell | Download + replace | +| `OSDCloud/core/driverpacks/hp.xml` | OEM XML snapshot | HP | Download + replace | +| `OSDCloud/core/driverpacks/lenovo.xml` | OEM XML snapshot | Lenovo | Download + replace | +| `OSDCloud/core/driverpacks/surface.json` | JSON array | Microsoft (Surface) | Manual edit / `.github/scripts/Update-MicrosoftCatalog.ps1` | +| `OSDCloud/core/driverpacks/panasonic.json` | Nested JSON | Panasonic | Manual edit | +| `OSDCloud/core/driverpacks/generic.json` | JSON array | ARM64 / multi-OEM | Manual edit | -OEM source URLs are defined in `module.json` under each manufacturer key (`driverpackcatalogoem`). -Dell, HP, and Lenovo catalogs are fetched at runtime by their respective `Get-OSDCloudCatalog*` functions; local files are fallback only. +OEM source URLs are defined in `OSDCloud/core/module.json` under each manufacturer key (`driverpackcatalogoem`). +Dell, HP, and Lenovo catalogs are fetched at runtime by their respective `Get-OSDCoreDriverPackCatalog*` functions; local files are fallback only. --- ### Updating Dell, HP, or Lenovo snapshots -At runtime, `Get-OSDCloudCatalogDell/Hp/Lenovo` downloads the OEM CAB/XML to `$env:TEMP`, parses it, and falls back to the local snapshot on failure. To refresh the committed snapshot: +At runtime, `Get-OSDCoreDriverPackCatalogDell/Hp/Lenovo` downloads the OEM CAB/XML to `$env:TEMP`, parses it, and falls back to the local snapshot on failure. To refresh the committed snapshot: 1. Download the OEM catalog CAB from the URL in `module.json`: - Dell: `https://downloads.dell.com/catalog/DriverPackCatalog.cab` @@ -123,9 +123,9 @@ At runtime, `Get-OSDCloudCatalogDell/Hp/Lenovo` downloads the OEM CAB/XML to `$e ``` 3. Replace the local file: - - `core/driverpacks/dell.xml` - - `core/driverpacks/hp.xml` - - `core/driverpacks/lenovo.xml` + - `OSDCloud/core/driverpacks/dell.xml` + - `OSDCloud/core/driverpacks/hp.xml` + - `OSDCloud/core/driverpacks/lenovo.xml` 4. Do not edit the OEM XML content manually — it is consumed as-is by the catalog parser. @@ -134,9 +134,9 @@ At runtime, `Get-OSDCloudCatalogDell/Hp/Lenovo` downloads the OEM CAB/XML to `$e ### surface.json (Surface driver packs — amd64 and arm64) Used for `Manufacturer == 'Microsoft'` on both AMD64 and ARM64 devices. -Read directly by `Get-OSDCloudCatalogSurface`, which enriches entries with live MSI URLs from each entry's `UpdatePage` at runtime. **There is no `surface.xml` to regenerate.** +Read directly by `Get-OSDCoreDriverPackCatalogSurface`, which enriches entries with live MSI URLs from each entry's `UpdatePage` at runtime. **There is no `surface.xml` to regenerate.** -`Update-MicrosoftCatalog.ps1` (run by the `update-catalog-microsoft.yaml` GitHub Actions workflow) scrapes all `UpdatePage` URLs and commits any changed MSI filenames, URLs, or release dates automatically each week. +The `.github/scripts/Update-MicrosoftCatalog.ps1` helper (run by the `.github/workflows/update-catalog-microsoft.yaml` workflow) scrapes all `UpdatePage` URLs and updates the JSON file when MSI filenames, URLs, or release dates change. #### JSON object schema @@ -168,7 +168,7 @@ Read directly by `Get-OSDCloudCatalogSurface`, which enriches entries with live ### panasonic.json (Panasonic driver packs) -This file uses a different nested schema compared to other driver pack catalogs. It is read by `Get-OSDCloudCatalogPanasonic`, which flattens the nested structure at runtime and excludes Windows 10 entries. +This file uses a different nested schema compared to other driver pack catalogs. It is read by `Get-OSDCoreDriverPackCatalogPanasonic`, which flattens the nested structure at runtime and excludes Windows 10 entries. #### JSON structure @@ -217,6 +217,6 @@ Key differences: ## Common mistakes - **Omitting the build switch case** — a new OS XML will load without errors but produce zero OS options in the UX because every `` hits the `default { continue }` branch. -- **Stale `surface.json` driver URLs** — `Get-OSDCloudCatalogSurface` serves MSI URLs from `surface.json`; if `UpdatePage` links change and the automated workflow has not run, deployed drivers may point to outdated or removed files. Run `Update-MicrosoftCatalog.ps1` manually to refresh. +- **Stale `surface.json` driver URLs** — `Get-OSDCoreDriverPackCatalogSurface` serves MSI URLs from `OSDCloud/core/driverpacks/surface.json`; if `UpdatePage` links change and the automated workflow has not run, deployed drivers may point to outdated or removed files. Run `.github/scripts/Update-MicrosoftCatalog.ps1` manually to refresh. - **Editing OEM XML snapshots manually** — Dell/HP/Lenovo XML is replaced wholesale from upstream; manual edits will be lost on the next snapshot refresh. - **Adding a Windows version string to workflow configs without the catalog XML** — the UX will offer the version but `Get-OSDCloudCoreOperatingSystems` will return no matching ESD entries, causing `Initialize-OSDCloudDeploy` to throw. diff --git a/.github/instructions/workflow-tasks.instructions.md b/.github/instructions/workflow-tasks.instructions.md index 96cb76f..2ca4480 100644 --- a/.github/instructions/workflow-tasks.instructions.md +++ b/.github/instructions/workflow-tasks.instructions.md @@ -135,7 +135,7 @@ function step-phase-action { # Implementation here. # Read context from $global:OSDCloudWorkflowInvoke (architecture, paths, OS object, etc.) # Read deployment config from $global:OSDCloudDeploy - # Read device info from $global:OSDCloudDevice + # Read device info from $global:OSDCoreDevice #endregion #================================================= diff --git a/.github/prompts/osdcloud-update-driverpack-catalog.prompt.md b/.github/prompts/osdcloud-update-driverpack-catalog.prompt.md index e3b34d5..598ffde 100644 --- a/.github/prompts/osdcloud-update-driverpack-catalog.prompt.md +++ b/.github/prompts/osdcloud-update-driverpack-catalog.prompt.md @@ -1,10 +1,10 @@ --- -description: "Step-by-step guide for updating OSDCloud driver pack catalogs: refresh Dell/HP/Lenovo XML snapshots, add or update Surface models in surface.json, update panasonic.json, or add ARM64 entries to generic.json." +description: "Step-by-step guide for updating OSDCloud driver pack catalogs: refresh Dell/HP/Lenovo XML snapshots, add or update Surface models in OSDCloud/core/driverpacks/surface.json, update OSDCloud/core/driverpacks/panasonic.json, or add ARM64 entries to OSDCloud/core/driverpacks/generic.json." argument-hint: "dell | hp | lenovo | microsoft | panasonic | generic | all" agent: "agent" --- -Perform a driver pack catalog update for OSDCloud following the rules in [catalog-update.instructions.md](../instructions/catalog-update.instructions.md). +Perform a driver pack catalog update for OSDCloud following the rules in [osdcloud-catalog-update.instructions.md](../instructions/osdcloud-catalog-update.instructions.md). ## Step 1 -- Determine scope @@ -12,12 +12,12 @@ If the user provided an argument, use it to determine which catalogs to update: | Argument | Catalog file(s) | |---|---| -| `dell` | `core/driverpacks/dell.xml` | -| `hp` | `core/driverpacks/hp.xml` | -| `lenovo` | `core/driverpacks/lenovo.xml` | -| `microsoft` | `core/driverpacks/surface.json` | -| `panasonic` | `core/driverpacks/panasonic.json` | -| `generic` | `core/driverpacks/generic.json` | +| `dell` | `OSDCloud/core/driverpacks/dell.xml` | +| `hp` | `OSDCloud/core/driverpacks/hp.xml` | +| `lenovo` | `OSDCloud/core/driverpacks/lenovo.xml` | +| `microsoft` | `OSDCloud/core/driverpacks/surface.json` | +| `panasonic` | `OSDCloud/core/driverpacks/panasonic.json` | +| `generic` | `OSDCloud/core/driverpacks/generic.json` | | `all` | All of the above | If no argument was provided, ask: **Which catalog(s) need updating?** (list the options above). @@ -28,7 +28,7 @@ If no argument was provided, ask: **Which catalog(s) need updating?** (list the For each OEM XML catalog being refreshed: -1. **Confirm the source URL** from `module.json`: +1. **Confirm the source URL** from `OSDCloud/core/module.json`: - Dell: `https://downloads.dell.com/catalog/DriverPackCatalog.cab` - HP: `https://hpia.hpcloud.hp.com/downloads/driverpackcatalog/HPClientDriverPackCatalog.cab` - Lenovo: `https://download.lenovo.com/cdrt/td/catalogv2.xml` (direct XML -- no CAB extraction needed) @@ -42,7 +42,7 @@ For each OEM XML catalog being refreshed: 3. **Replace the local snapshot**: ```powershell - Copy-Item "$env:TEMP\DriverPackCatalog.xml" 'core\driverpacks\dell.xml' -Force + Copy-Item "$env:TEMP\DriverPackCatalog.xml" 'OSDCloud\core\driverpacks\dell.xml' -Force ``` 4. **Do not edit the XML content** -- it is consumed as-is. @@ -53,7 +53,7 @@ For each OEM XML catalog being refreshed: ## Microsoft (Surface) -- surface.json -`surface.json` is the sole source of truth for Surface driver packs. `Get-OSDCloudCatalogSurface` reads it directly at runtime and enriches entries from live download pages for models that have an `UpdatePage` URL. **There is no `surface.xml` to regenerate.** +`surface.json` is the sole source of truth for Surface driver packs. `Get-OSDCoreDriverPackCatalogSurface` reads it directly at runtime and enriches entries from live download pages for models that have an `UpdatePage` URL. **There is no `surface.xml` to regenerate.** ### If adding or updating a model entry manually @@ -74,12 +74,12 @@ Then: 1. Set `CatalogVersion` and `ReleaseDate` to today's date in `YY.MM.DD` format. 2. Set `Name` to `"Surface []"` (no "Microsoft" prefix in the Name field). -3. Edit `core/driverpacks/surface.json` -- insert the new entry in the correct position (sort by Model name). +3. Edit `OSDCloud/core/driverpacks/surface.json` -- insert the new entry in the correct position (sort by Model name). 4. Confirm the file is saved. ### Automated update via GitHub Actions -The `update-catalog-microsoft.yaml` workflow runs `.github/scripts/Update-MicrosoftCatalog.ps1` every 4 hours and on demand. It scrapes every `UpdatePage` URL in `surface.json`, selects the best available MSI, and commits any changes automatically. Run `workflow_dispatch` to trigger an immediate update. +The `.github/workflows/update-catalog-microsoft.yaml` workflow runs `.github/scripts/Update-MicrosoftCatalog.ps1` every 4 hours and on demand. It scrapes every `UpdatePage` URL in `OSDCloud/core/driverpacks/surface.json`, selects the best available MSI, and updates the JSON file when changes are detected. Trigger the workflow manually with `workflow_dispatch` to run it immediately. --- @@ -125,9 +125,9 @@ Rules: After all edits, confirm each item: - [ ] OEM XML snapshots replaced (Dell/HP/Lenovo as applicable) -- [ ] `surface.json` edited (if applicable) -- [ ] `panasonic.json` updated with new `LastDateModified` (if applicable) -- [ ] `generic.json` updated (if applicable) +- [ ] `OSDCloud/core/driverpacks/surface.json` edited (if applicable) +- [ ] `OSDCloud/core/driverpacks/panasonic.json` updated with new `LastDateModified` (if applicable) +- [ ] `OSDCloud/core/driverpacks/generic.json` updated (if applicable) - [ ] No manual edits made to Dell/HP/Lenovo XML content Report a one-line summary of each file changed and what was updated. diff --git a/.github/skills/add-surface-model/SKILL.md b/.github/skills/add-surface-model/SKILL.md index 5b5002c..c4ed846 100644 --- a/.github/skills/add-surface-model/SKILL.md +++ b/.github/skills/add-surface-model/SKILL.md @@ -1,6 +1,6 @@ --- name: add-surface-model -description: "Use when adding or validating a Microsoft Surface model in OSDCloud surface.json, resolving System SKU/SystemId, Surface driver pack UpdatePage, direct MSI Url, FileName, ReleaseDate, OSArchitecture, and catalog properties." +description: "Use when adding or validating a Microsoft Surface model in OSDCloud/core/driverpacks/surface.json, resolving System SKU/SystemId, Surface driver pack UpdatePage, direct MSI Url, FileName, ReleaseDate, OSArchitecture, and catalog properties." argument-hint: "Surface model name, for example: Surface Laptop 8 Intel" --- @@ -12,17 +12,17 @@ Use this skill to add or validate Microsoft Surface driver pack entries in `OSDC 1. Review the current catalog entry shape in `OSDCloud/core/driverpacks/surface.json` and use [the template](./assets/surface-model-template.json) for field order. 2. Follow [the Surface catalog rules](./references/surface-catalog-rules.md) to resolve each property from Microsoft sources and local catalog conventions. -3. Add or update one JSON object in `surface.json`, keeping entries sorted by `Model`. -4. Run `.\.github\scripts\Update-MicrosoftCatalog.ps1 -JsonPath 'OSDCloud\core\driverpacks\surface.json'` to refresh `FileName`, `Url`, and `ReleaseDate` from `UpdatePage`. -5. Validate JSON parsing, direct MSI URL shape, `FileName`/`Url` consistency, and runtime loading through `Get-OSDCloudCatalogSurface` when practical. +3. Add or update one JSON object in `OSDCloud/core/driverpacks/surface.json`, keeping entries sorted by `Model`. +4. Run `./.github/scripts/Update-MicrosoftCatalog.ps1 -JsonPath 'OSDCloud/core/driverpacks/surface.json'` to refresh `FileName`, `Url`, and `ReleaseDate` from `UpdatePage`. +5. Validate JSON parsing, direct MSI URL shape, `FileName`/`Url` consistency, and runtime loading through `Get-OSDCoreDriverPackCatalogSurface` when practical. ## Required Sources - System model and System SKU reference: `https://learn.microsoft.com/en-us/surface/surface-system-sku-reference` - Surface driver and firmware update pages: `https://learn.microsoft.com/en-us/surface/manage-surface-driver-and-firmware-updates` - Support model pages linked from Microsoft Surface driver pages when they expose clearer Download Center IDs. -- Repo updater behavior in `.github/scripts/Update-MicrosoftCatalog.ps1`. +- Repo updater behavior in `.github/scripts/Update-MicrosoftCatalog.ps1`, including its fallback to the Microsoft confirmation page when no MSI links are found on the details page. ## Stop Conditions -Do not leave placeholder values in `surface.json`. If the Microsoft Download Center page cannot yield a direct `.msi` URL through the updater or manual verification, pause and report the blocked fields instead of committing guessed values. +Do not leave placeholder values in `OSDCloud/core/driverpacks/surface.json`. If the Microsoft Download Center page cannot yield a direct `.msi` URL through the updater or manual verification, pause and report the blocked fields instead of committing guessed values. diff --git a/.github/skills/add-surface-model/assets/surface-model-template.json b/.github/skills/add-surface-model/assets/surface-model-template.json index 2204c28..b1ebbc9 100644 --- a/.github/skills/add-surface-model/assets/surface-model-template.json +++ b/.github/skills/add-surface-model/assets/surface-model-template.json @@ -1,5 +1,5 @@ { - "CatalogVersion": "", + "CatalogVersion": "", "ReleaseDate": "", "Name": " []", "Manufacturer": "Microsoft", diff --git a/.github/skills/add-surface-model/references/surface-catalog-rules.md b/.github/skills/add-surface-model/references/surface-catalog-rules.md index e2aee9e..caf34ea 100644 --- a/.github/skills/add-surface-model/references/surface-catalog-rules.md +++ b/.github/skills/add-surface-model/references/surface-catalog-rules.md @@ -2,7 +2,7 @@ ## Field Resolution -Use the current `surface.json` object order for every entry: +Use the current `OSDCloud/core/driverpacks/surface.json` object order for every entry: 1. `CatalogVersion` is the date the entry is added or manually changed in the catalog, formatted as `yy.MM.dd`. 2. `ReleaseDate` is the DriverPack release date from the Microsoft Download Center page Date Published value, formatted as `yy.MM.dd`. If the page does not expose Date Published but the direct MSI is verified, use the MSI response `Last-Modified` date and note that fallback in validation. @@ -21,7 +21,7 @@ Use the current `surface.json` object order for every entry: Start at `https://learn.microsoft.com/en-us/surface/manage-surface-driver-and-firmware-updates`. If the table does not expose the direct Download Center details URL clearly, follow the support page for the Surface family and choose the row that best matches the model and processor family. -After adding or updating `UpdatePage`, run `.\.github\scripts\Update-MicrosoftCatalog.ps1 -JsonPath 'OSDCloud\core\driverpacks\surface.json'`. The script is the source of truth for picking the direct MSI: +After adding or updating `UpdatePage`, run `./.github/scripts/Update-MicrosoftCatalog.ps1 -JsonPath 'OSDCloud/core/driverpacks/surface.json'`. The script is the source of truth for picking the direct MSI: - Scrape `UpdatePage` for `https://download.microsoft.com/download/.../*.msi` links. - If none are present on the details page, try `https://www.microsoft.com/en-us/download/confirmation.aspx?id=`. @@ -31,7 +31,7 @@ After adding or updating `UpdatePage`, run `.\.github\scripts\Update-MicrosoftCa ## Validation Checklist -- `surface.json` parses with `ConvertFrom-Json`. +- `OSDCloud/core/driverpacks/surface.json` parses with `ConvertFrom-Json`. - The entry contains all template fields in order. - `Name` uses the same `ReleaseDate` as the object. - `Manufacturer` is `Microsoft`. @@ -43,4 +43,4 @@ After adding or updating `UpdatePage`, run `.\.github\scripts\Update-MicrosoftCa ## Runtime Notes -`Get-OSDCloudCatalogSurface` reads `surface.json`, enriches entries with live data from `UpdatePage`, and caches results in `$env:TEMP\osdcloud-driverpack-surface.json`. During deployment, Surface matching depends on `SystemId`, so incorrect System SKU values prevent the correct driver pack from being selected. +`Get-OSDCoreDriverPackCatalogSurface` reads `OSDCloud/core/driverpacks/surface.json`, enriches entries with live data from `UpdatePage`, and caches results in `$env:TEMP\osdcloud-driverpack-surface.json`. During deployment, Surface matching depends on `SystemId`, so incorrect System SKU values prevent the correct driver pack from being selected. diff --git a/CHANGELOG.md b/CHANGELOG.md index ed1d181..66b5dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,33 @@ All notable changes to this project will be documented in this file. -## Unreleased +## 26.8.5.1 - August 5, 2026 + +### Added + +- Added core device cache helpers: `Get-OSDCoreCacheContent`, `Get-OSDCoreCacheDrive`, `Get-OSDCoreCacheUSBPath`, and `Test-OSDCoreCacheUSB`. +- Added `Update-RecastOSDCloudUSBCache` for USB cache refresh and maintenance workflows. +- Added `ConvertTo-TrimmedString` private helper for normalized string handling. +- Added `Get-ModuleCoreDriverPacks` and `Get-OSDCoreDriverPackCatalogSurface` to the reorganized core driver pack pipeline. +- Added `Get-OSDCoreOperatingSystems` and `Set-OSDCoreOperatingSystemCloudObject` for core operating system catalog selection. + +### Changed + +- Module version bumped to `26.8.5.1`. +- Refactored core device initialization into `Initialize-OSDCoreDevice` and updated deployment flow wiring. +- Reorganized core helper layout by moving driver pack and operating system catalog functions out of `private/core/` into `private/core-driverpack/` and `private/core-operatingsystem/`. +- Updated `Initialize-OSDCloudDeploy` to consume `Get-ModuleCoreDriverPacks`. +- Renamed and relocated date/time sync helper from `Sync-OSDCloudDateTime` to `Sync-OSDCoreDateTime`. +- Updated `Show-OSDCloudDeviceInfo` implementation and related help output. +- Refreshed workflow and UX assets across classic, default, dev, insiders, and vNext channels, including `MainWindow.xaml` updates. +- Updated disk, workflow task, and driver step scripts for alignment with the reorganized core helper model. +- Updated repository documentation and guidance including `README.md`, Copilot instructions, workflow task instructions, and Surface catalog maintenance skill assets. + +### Removed + +- Removed legacy `Get-OSDCloudCoreDriverPacks` and legacy `Get-OSDCloudCatalogSurface` private core helpers. +- Removed legacy `Sync-InternetDateTime` helper. +- Removed standalone `Get-OSDCloudCache` markdown help page and aligned module help XML. ## 26.8.3.1 - August 3, 2026 diff --git a/OSDCloud/OSDCloud.psd1 b/OSDCloud/OSDCloud.psd1 index efdcb78..13035fe 100644 --- a/OSDCloud/OSDCloud.psd1 +++ b/OSDCloud/OSDCloud.psd1 @@ -8,7 +8,7 @@ @{ # --- Identity --- RootModule = 'OSDCloud.psm1' - ModuleVersion = '26.8.3.1' + ModuleVersion = '26.8.5.1' CompatiblePSEditions = @('Core','Desktop') GUID = '2fbd5c65-79c7-4561-9a2e-c4a4eebc89c7' @@ -16,7 +16,10 @@ Author = 'David Segura' CompanyName = 'Recast Software' Copyright = '(c) 2026 Recast Software. All rights reserved.' - Description = 'OSDCloud PowerShell Module for OS Deployment in the Cloud.' + Description = @' +PowerShell module for OSDCloud v2 deployment automation and WinPE Startup from recastsoftware.com. +Includes operating system, driver pack, and device provisioning workflows. +'@ # --- Requirements --- PowerShellVersion = '5.1' @@ -25,7 +28,6 @@ FunctionsToExport = @( # WinOS 'Deploy-OSDCloud', - 'Get-OSDCloudCache', 'Get-OSDCloudModulePath', 'Get-OSDCloudModuleVersion', 'Show-OSDCloudDeviceInfo', diff --git a/OSDCloud/docs/Get-OSDCloudCache.md b/OSDCloud/docs/Get-OSDCloudCache.md deleted file mode 100644 index 48723b3..0000000 --- a/OSDCloud/docs/Get-OSDCloudCache.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -external help file: OSDCloud-help.xml -Module Name: OSDCloud -online version: https://github.com/OSDeploy/OSDCloud/blob/main/PRIVACY.md -schema: 2.0.0 ---- - -# Get-OSDCloudCache - -## SYNOPSIS -Returns OSDCloud cache paths or cached content found on local file system drives. - -## SYNTAX - -``` -Get-OSDCloudCache [[-Type] ] [-ProgressAction ] [] -``` - -## DESCRIPTION -Enumerates mounted file system drives and discovers OSDCloud cache content. -Returns objects with Type, FullName, SizeMB, -DriveRoot, VolumeLabel, and VolumeUniqueId properties. - -If Type is omitted, returns discovered '\:\OSDCloud' cache root -folders as Type 'Cache'. - -Type values: -- ESD: All .esd files under '\:\OSDCloud\OS' recursively. -- ISO: All .iso files under '\:\OSDCloud\ISO' recursively. -- DriverPacks: All .cab, .exe, .msi, and .zip files under - '\:\OSDCloud\DriverPacks' recursively. -- Drivers: Immediate folders under '\:\OSDCloud\Drivers' that - contain at least one .inf file in any child folder. - - Profiles: Immediate folders under '\:\OSDCloud\Profiles'. -- WIM: All .wim files under '\:\OSDCloud\WIM' recursively. -- *: Includes all supported Type values. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-OSDCloudCache -``` - -Returns paths such as 'C:\OSDCloud' and 'D:\OSDCloud' when present. - -### EXAMPLE 2 -``` -Get-OSDCloudCache -Type ESD -``` - -Returns all .esd files under each discovered cache OS folder. - -### EXAMPLE 3 -``` -Get-OSDCloudCache -Type ESD,DriverPacks -``` - -Returns all .esd files and driver pack files from each discovered cache. - -### EXAMPLE 4 -``` -Get-OSDCloudCache -Type * -``` - -Returns all supported cache content types. - -## PARAMETERS - -### -Type -Optional cache content selector. - -Supports one or more values. -Use '*' to return all supported -cache content types. - -```yaml -Type: String[] -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -### System.Object[]. Objects with Type, FullName, SizeMB, -### DriveRoot, VolumeLabel, and VolumeUniqueId. -## NOTES - -## RELATED LINKS diff --git a/OSDCloud/docs/Show-OSDCloudDeviceInfo.md b/OSDCloud/docs/Show-OSDCloudDeviceInfo.md index 18b4f77..65caa95 100644 --- a/OSDCloud/docs/Show-OSDCloudDeviceInfo.md +++ b/OSDCloud/docs/Show-OSDCloudDeviceInfo.md @@ -80,7 +80,7 @@ Log Files Created: Functions Called: - Get-OSDCloudModuleVersion: Retrieves current OSDCloud module version -- Initialize-OSDCloudDevice: Populates $global:OSDCloudDevice with hardware details +- Initialize-OSDCoreDevice: Populates $global:OSDCoreDevice with hardware details The function updates the window title to '\[OSDCloud\] - WinPE and Device Information' to indicate the current operation status. diff --git a/OSDCloud/en-US/OSDCloud-help.xml b/OSDCloud/en-US/OSDCloud-help.xml index 945814e..a7a8fdb 100644 --- a/OSDCloud/en-US/OSDCloud-help.xml +++ b/OSDCloud/en-US/OSDCloud-help.xml @@ -40,123 +40,10 @@ False - - ProgressAction - - {{ Fill ProgressAction Description }} - - ActionPreference - - ActionPreference - - - None - - - - - - WorkflowName - - The name of the OSDCloud workflow to run. Defaults to 'default'. Available workflows are located in the module's workflow folder. - - String - - String - - - Default - - - CLI - - Skips the graphical UX and runs the deployment workflow immediately in the current console session. - - SwitchParameter - - SwitchParameter - - - False - - - ProgressAction - - {{ Fill ProgressAction Description }} - - ActionPreference - - ActionPreference - - - None - - - - - - - System.Void - - - - - - - - - This command writes deployment status to the host and starts workflow tasks. In GUI mode, workflow execution starts only after the operator clicks Start. - - - - - -------------------------- EXAMPLE 1 -------------------------- - Deploy-OSDCloud - - Launches the OSDCloud graphical UX for the default workflow. The deployment starts only after the operator clicks Start in the UI. - - - - -------------------------- EXAMPLE 2 -------------------------- - Deploy-OSDCloud -CLI - - Runs the default OSDCloud workflow immediately without the graphical UX. - - - - -------------------------- EXAMPLE 3 -------------------------- - Deploy-OSDCloud -WorkflowName 'latest' - - Launches the graphical UX for the 'latest' workflow. - - - - - - https://github.com/OSDeploy/OSDCloud/blob/main/PRIVACY.md - https://github.com/OSDeploy/OSDCloud/blob/main/PRIVACY.md - - - - - - Deploy-OSDCloudCLI - Deploy - OSDCloudCLI - - Starts an OSDCloud operating system deployment in CLI mode. - - - - Initializes and runs an OSDCloud deployment workflow directly in the current console session without launching the graphical UX. This function is a CLI-only entry point and immediately invokes workflow tasks after initialization. - - - - Deploy-OSDCloudCLI - SkipFirmwareUpdate + Force - Skips firmware update download and apply steps in the workflow. + Suppresses supported confirmation prompts for destructive workflow steps. SwitchParameter @@ -165,15 +52,16 @@ False - Force + ProfileName - Skips confirmation prompts for destructive workflow steps that support force behavior. + The full OS profile name used to resolve the Env file path. Defaults to 'default'. Ignored in WinPE. + String - SwitchParameter + String - False + Default OperatingSystem @@ -250,10 +138,22 @@ + + WorkflowName + + The name of the OSDCloud workflow to run. Defaults to 'default'. Available workflows are located in the module's workflow folder. + + String + + String + + + Default + - SkipFirmwareUpdate + CLI - Skips firmware update download and apply steps in the workflow. + Skips the graphical UX and runs the deployment workflow immediately in the current console session. SwitchParameter @@ -265,7 +165,7 @@ Force - Skips confirmation prompts for destructive workflow steps that support force behavior. + Suppresses supported confirmation prompts for destructive workflow steps. SwitchParameter @@ -274,6 +174,18 @@ False + + ProfileName + + The full OS profile name used to resolve the Env file path. Defaults to 'default'. Ignored in WinPE. + + String + + String + + + Default + OperatingSystem @@ -360,192 +272,49 @@ - This function does not display the graphical UX. Workflow execution begins immediately after initialization. + This command writes deployment status to the host and starts workflow tasks. In GUI mode, workflow execution starts only after the operator clicks Start. Runtime parameters are provided by Get-OSDCloudWorkflowRuntimeParameter. -------------------------- EXAMPLE 1 -------------------------- - Deploy-OSDCloudCLI + Deploy-OSDCloud - Runs the default OSDCloud workflow immediately in the current console session. + Launches the OSDCloud graphical UX for the default workflow. The deployment starts only after the operator clicks Start in the UI. -------------------------- EXAMPLE 2 -------------------------- - Deploy-OSDCloudCLI -OperatingSystem 'Windows 11 24H2' + Deploy-OSDCloud -CLI - Runs the CLI workflow and overrides the OperatingSystem default. + Runs the default OSDCloud workflow immediately without the graphical UX. -------------------------- EXAMPLE 3 -------------------------- - Deploy-OSDCloudCLI -OSEdition 'Enterprise' -OSLanguageCode 'en-gb' + Deploy-OSDCloud -WorkflowName 'latest' - Runs the CLI workflow with Enterprise edition and en-gb language. + Launches the graphical UX for the 'latest' workflow. -------------------------- EXAMPLE 4 -------------------------- - Deploy-OSDCloudCLI -Task 'OSDCloud SkipFirmwareUpdate' + Deploy-OSDCloud -CLI -OperatingSystem 'Windows 11 24H2' -OSEdition 'Enterprise' - Runs the selected CLI workflow task. + Runs in CLI mode using dynamic runtime overrides from the selected workflow. -------------------------- EXAMPLE 5 -------------------------- - Deploy-OSDCloudCLI -SkipFirmwareUpdate - - Runs the default workflow and skips firmware update steps. - - - - -------------------------- EXAMPLE 6 -------------------------- - Deploy-OSDCloudCLI -Force - - Runs the default workflow and suppresses supported confirmation prompts. - - - - - - Online Version: - https://github.com/OSDeploy/OSDCloud/blob/main/PRIVACY.md - - - - - - Get-OSDCloudCache - Get - OSDCloudCache - - Returns OSDCloud cache paths or cached content found on local file system drives. - - - - Enumerates mounted file system drives and discovers OSDCloud cache content. Returns objects with Type, FullName, SizeMB, DriveRoot, VolumeLabel, and VolumeUniqueId properties. - If Type is omitted, returns discovered '<DriveLetter>:\OSDCloud' cache root folders as Type 'Cache'. - Type values: - ESD: All .esd files under '<DriveLetter>:\OSDCloud\OS' recursively. - - ISO: All .iso files under '<DriveLetter>:\OSDCloud\ISO' recursively. - - DriverPacks: All .cab, .exe, .msi, and .zip files under - '<DriveLetter>:\OSDCloud\DriverPacks' recursively. - Drivers: Immediate folders under '<DriveLetter>:\OSDCloud\Drivers' that contain at least one .inf file in any child folder. - Profiles: Immediate folders under '<DriveLetter>:\OSDCloud\Profiles'. - WIM: All .wim files under '<DriveLetter>:\OSDCloud\WIM' recursively. - - *: Includes all supported Type values. - - - - Get-OSDCloudCache - - Type - - Optional cache content selector. - Supports one or more values. Use '*' to return all supported cache content types. - - String[] - - String[] - - - None - - - ProgressAction - - {{ Fill ProgressAction Description }} - - ActionPreference - - ActionPreference - - - None - - - - - - Type - - Optional cache content selector. - Supports one or more values. Use '*' to return all supported cache content types. - - String[] - - String[] - - - None - - - ProgressAction - - {{ Fill ProgressAction Description }} - - ActionPreference - - ActionPreference - - - None - - - - - - - System.Object[]. Objects with Type, FullName, SizeMB, - - - - - - - - DriveRoot, VolumeLabel, and VolumeUniqueId. - - - - - - - - - - - - - - -------------------------- EXAMPLE 1 -------------------------- - Get-OSDCloudCache + Deploy-OSDCloud -ProfileName 'Lab' - Returns paths such as 'C:\OSDCloud' and 'D:\OSDCloud' when present. - - - - -------------------------- EXAMPLE 2 -------------------------- - Get-OSDCloudCache -Type ESD - - Returns all .esd files under each discovered cache OS folder. - - - - -------------------------- EXAMPLE 3 -------------------------- - Get-OSDCloudCache -Type ESD,DriverPacks - - Returns all .esd files and driver pack files from each discovered cache. - - - - -------------------------- EXAMPLE 4 -------------------------- - Get-OSDCloudCache -Type * - - Returns all supported cache content types. + Launches the OSDCloud graphical UX using the 'Lab' profile Env path. - Online Version: + https://github.com/OSDeploy/OSDCloud/blob/main/PRIVACY.md https://github.com/OSDeploy/OSDCloud/blob/main/PRIVACY.md @@ -775,7 +544,7 @@ - Win32_NetworkAdapter.txt: Complete network adapter information Functions Called: - Get-OSDCloudModuleVersion: Retrieves current OSDCloud module version - - Initialize-OSDCloudDevice: Populates $global:OSDCloudDevice with hardware details + - Initialize-OSDCoreDevice: Populates $global:OSDCoreDevice with hardware details The function updates the window title to '[OSDCloud] - WinPE and Device Information' to indicate the current operation status. @@ -887,4 +656,4 @@ Runs Show-OSDCloudDeviceInfo to display comprehensive WinPE and device informati - + \ No newline at end of file diff --git a/OSDCloud/private/ConvertTo-TrimmedString.ps1 b/OSDCloud/private/ConvertTo-TrimmedString.ps1 new file mode 100644 index 0000000..85ced54 --- /dev/null +++ b/OSDCloud/private/ConvertTo-TrimmedString.ps1 @@ -0,0 +1,36 @@ +function ConvertTo-TrimmedString { + <# + .SYNOPSIS + Converts input to a trimmed string. + + .DESCRIPTION + Accepts pipeline or direct input, converts non-null values to string, and + returns the value with leading and trailing whitespace removed. Null input + is returned as null. + + .PARAMETER Value + Value to convert to string and trim. + + .EXAMPLE + ConvertTo-TrimmedString -Value ' hello ' + Returns 'hello'. + + .LINK + https://github.com/OSDeploy/OSD/tree/master/docs + + .NOTES + Author: David Segura - Recast Software + 2026-07-16 - Moved help block inside function and normalized required sections + #> + param( + [Parameter(ValueFromPipeline = $true)] + $Value + ) + + process { + if ($null -eq $Value) { + return $null + } + return $Value.ToString().Trim() + } +} diff --git a/OSDCloud/private/Initialize-OSDCloudDeploy.ps1 b/OSDCloud/private/Initialize-OSDCloudDeploy.ps1 index 73a39a4..e1eb8e3 100644 --- a/OSDCloud/private/Initialize-OSDCloudDeploy.ps1 +++ b/OSDCloud/private/Initialize-OSDCloudDeploy.ps1 @@ -55,9 +55,9 @@ function Initialize-OSDCloudDeploy { # Limit to the first disk found $DeploymentDiskObject = $DeploymentDiskObject | Select-Object -First 1 #================================================= - # OSDCloudDevice + # OSDCoreDevice <# - PS C:\Users\david> $OSDCloudDevice + PS C:\Users\david> $OSDCoreDevice Name Value ---- ----- OSDManufacturer HP @@ -104,9 +104,8 @@ function Initialize-OSDCloudDeploy { TpmSpecVersion 2.0, 0, 1.59 UUID 048A2C6B-A1D9-488C-8BF0-18D0F9A82D91 #> - if (-not ($global:OSDCloudDevice)) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Initialize OSDCloud Device" - Initialize-OSDCloudDevice + if (-not ($global:OSDCoreDevice)) { + Initialize-OSDCoreDevice } #================================================= # OSDCloudWorkflowTasks @@ -134,7 +133,7 @@ function Initialize-OSDCloudDeploy { # Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Get OSDCloud OperatingSystems" # Limit to matching Processor Architecture - $ProcessorArchitecture = $global:OSDCloudDevice.ProcessorArchitecture + $ProcessorArchitecture = $global:OSDCoreDevice.ProcessorArchitecture $global:DeployOSDCloudOperatingSystems = Get-OSDCloudCoreOperatingSystems | Where-Object { $_.OSArchitecture -match "$ProcessorArchitecture" } # Need to fail if no OS found for Architecture @@ -208,10 +207,10 @@ function Initialize-OSDCloudDeploy { $ImageFileUrl = $OperatingSystemObject.FilePath #================================================= # DriverPacks - $OSDManufacturer = $global:OSDCloudDevice.OSDManufacturer - $OSDModel = $global:OSDCloudDevice.OSDModel - $OSDProduct = $global:OSDCloudDevice.OSDProduct - $DriverPackValues = Get-OSDCloudCoreDriverPacks + $OSDManufacturer = $global:OSDCoreDevice.OSDManufacturer + $OSDModel = $global:OSDCoreDevice.OSDModel + $OSDProduct = $global:OSDCoreDevice.OSDProduct + $DriverPackValues = Get-ModuleCoreDriverPacks $DriverPackObject = $DriverPackValues | Where-Object { $_.SystemId -match $OSDProduct } | Select-Object -First 1 if ($DriverPackObject) { diff --git a/OSDCloud/public/WinOS/Get-OSDCloudCache.ps1 b/OSDCloud/private/core-device/Get-OSDCoreCacheContent.ps1 similarity index 70% rename from OSDCloud/public/WinOS/Get-OSDCloudCache.ps1 rename to OSDCloud/private/core-device/Get-OSDCoreCacheContent.ps1 index 0964733..f3996fa 100644 --- a/OSDCloud/public/WinOS/Get-OSDCloudCache.ps1 +++ b/OSDCloud/private/core-device/Get-OSDCoreCacheContent.ps1 @@ -1,15 +1,15 @@ -function Get-OSDCloudCache { +function Get-OSDCoreCacheContent { <# .SYNOPSIS - Returns OSDCloud cache paths or cached content found on local file system drives. + Returns cached OSDCloud content found on local file system drives. .DESCRIPTION Enumerates mounted file system drives and discovers OSDCloud cache content. - Returns objects with Type, FullName, SizeMB, - DriveRoot, VolumeLabel, and VolumeUniqueId properties. + Returns objects with Type, Name, FullName, SizeMB, + DriveRoot, VolumeLabel, VolumeUniqueId, and USB properties. + Exports the returned object to $env:Temp\OSDCoreCacheContent.xml each time the function runs. - If Type is omitted, returns discovered ':\OSDCloud' cache root - folders as Type 'Cache'. + If Type is omitted, retur ns all supported cache content types. Type values: - ESD: All .esd files under ':\OSDCloud\OS' recursively. @@ -28,40 +28,66 @@ function Get-OSDCloudCache { Supports one or more values. Use '*' to return all supported cache content types. + .PARAMETER Include + Optional list of drive letters to include when searching for OSDCloud cache + content. Accepts values such as 'C', 'D:', or 'E:\'. + + When omitted, all mounted file system drive letters are considered. + + .PARAMETER Exclude + Optional list of drive letters to exclude when searching for OSDCloud cache + content. Accepts values such as 'C', 'D:', or 'E:\'. + + Excluded drives are skipped even when they are also present in Include. + .OUTPUTS - System.Object[]. Objects with Type, FullName, SizeMB, - DriveRoot, VolumeLabel, and VolumeUniqueId. + System.Object[]. Objects with Type, Name, FullName, SizeMB, + DriveRoot, VolumeLabel, VolumeUniqueId, and USB. .EXAMPLE - Get-OSDCloudCache + Get-OSDCoreCacheContent - Returns paths such as 'C:\OSDCloud' and 'D:\OSDCloud' when present. + Returns all supported cache content types. .EXAMPLE - Get-OSDCloudCache -Type ESD + Get-OSDCoreCacheContent -Type ESD Returns all .esd files under each discovered cache OS folder. .EXAMPLE - Get-OSDCloudCache -Type ESD,DriverPacks + Get-OSDCoreCacheContent -Type ESD,DriverPacks Returns all .esd files and driver pack files from each discovered cache. .EXAMPLE - Get-OSDCloudCache -Type * + Get-OSDCoreCacheContent -Type * Returns all supported cache content types. + + .EXAMPLE + Get-OSDCoreCacheContent -Include C,D -Exclude D + + Searches only drive C for supported cache content types. #> [CmdletBinding()] [OutputType([System.Object[]])] param ( [Parameter()] [ValidateSet('ESD', 'ISO', 'DriverPacks', 'Drivers', 'Profiles', 'WIM', '*')] - [string[]]$Type + [string[]]$Type, + + [Parameter()] + [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] + [string[]]$Include, + + [Parameter()] + [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] + [string[]]$Exclude ) $Error.Clear() Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" + # Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDCoreCacheContent is updating" function Get-FileOnlySizeMB { param( @@ -97,11 +123,18 @@ function Get-OSDCloudCache { ) $volume = $null + $usb = $false if ($DriveRoot -match '^[A-Z]:\\$') { try { - $volume = Get-Volume -DriveLetter $DriveRoot.Substring(0, 1) -ErrorAction Stop + $driveLetter = $DriveRoot.Substring(0, 1) + $volume = Get-Volume -DriveLetter $driveLetter -ErrorAction Stop + $usb = [bool](Get-Disk -ErrorAction SilentlyContinue | + Where-Object { $_.BusType -eq 'USB' } | + Get-Partition -ErrorAction SilentlyContinue | + Where-Object { $_.DriveLetter -eq $driveLetter }) } catch { $volume = $null + $usb = $false } } @@ -109,6 +142,7 @@ function Get-OSDCloudCache { DriveRoot = $DriveRoot VolumeLabel = if ($volume) { [string]$volume.FileSystemLabel } else { $null } VolumeUniqueId = if ($volume) { [string]$volume.UniqueId } else { $null } + USB = [bool]$usb } } @@ -125,17 +159,52 @@ function Get-OSDCloudCache { ) [PSCustomObject]@{ - Type = $ResultType + Name = Split-Path -Path $ResultFullName -Leaf FullName = $ResultFullName SizeMB = Get-FileOnlySizeMB -Path $ResultFullName + Type = $ResultType + USB = [bool]$VolumeMetadata.USB DriveRoot = [string]$VolumeMetadata.DriveRoot VolumeLabel = [string]$VolumeMetadata.VolumeLabel VolumeUniqueId = [string]$VolumeMetadata.VolumeUniqueId } } + function ConvertTo-DriveLetterList { + param( + [Parameter()] + [string[]]$DriveLetters + ) + + $normalized = foreach ($driveLetter in $DriveLetters) { + if ([string]::IsNullOrWhiteSpace($driveLetter)) { + continue + } + + $value = $driveLetter.Trim().TrimEnd('\\') + if ($value.Length -lt 1) { + continue + } + + $letter = $value.Substring(0, 1).ToUpperInvariant() + if ($letter -match '^[A-Z]$') { + $letter + } + } + + @($normalized | Sort-Object -Unique) + } + + $includedDriveLetters = ConvertTo-DriveLetterList -DriveLetters $Include + $excludedDriveLetters = ConvertTo-DriveLetterList -DriveLetters $Exclude + $cachePaths = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Root -match '^[A-Z]:\\$' } | + Where-Object { + $driveLetter = ([string]$_.Root).Substring(0, 1).ToUpperInvariant() + (($includedDriveLetters.Count -eq 0) -or ($driveLetter -in $includedDriveLetters)) -and + ($driveLetter -notin $excludedDriveLetters) + } | ForEach-Object { $driveRoot = [string]$_.Root $osdCloudPath = Join-Path -Path $driveRoot -ChildPath 'OSDCloud' @@ -151,18 +220,11 @@ function Get-OSDCloudCache { $cachePaths = @($cachePaths | Sort-Object -Property CachePath -Unique) - if (-not $PSBoundParameters.ContainsKey('Type')) { - $result = foreach ($cacheEntry in $cachePaths) { - New-CacheResultObject -ResultType 'Cache' -ResultFullName ([string]$cacheEntry.CachePath) -VolumeMetadata $cacheEntry.VolumeMetadata - } - - $result = @($result | Sort-Object -Property FullName, Type -Unique | Sort-Object -Property FullName) - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found $($result.Count) path(s)" - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" - return $result + $selectedTypes = if ($PSBoundParameters.ContainsKey('Type')) { + @($Type | Sort-Object -Unique) + } else { + @('ESD', 'ISO', 'DriverPacks', 'Drivers', 'Profiles', 'WIM') } - - $selectedTypes = @($Type | Sort-Object -Unique) if ($selectedTypes -contains '*') { $selectedTypes = @('ESD', 'ISO', 'DriverPacks', 'Drivers', 'Profiles', 'WIM') } @@ -249,6 +311,7 @@ function Get-OSDCloudCache { } $result = @($result | Sort-Object -Property FullName, Type -Unique | Sort-Object -Property FullName) + $result | Export-Clixml -Path (Join-Path -Path $env:Temp -ChildPath 'OSDCoreCacheContent.xml') -Force Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found $($result.Count) path(s)" Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" diff --git a/OSDCloud/private/core-device/Get-OSDCoreCacheDrive.ps1 b/OSDCloud/private/core-device/Get-OSDCoreCacheDrive.ps1 new file mode 100644 index 0000000..6d65ffb --- /dev/null +++ b/OSDCloud/private/core-device/Get-OSDCoreCacheDrive.ps1 @@ -0,0 +1,129 @@ +function Get-OSDCoreCacheDrive { + <# + .SYNOPSIS + Returns OSDCloud cache drive metadata from local file system drives. + + .DESCRIPTION + Enumerates mounted file system drives that contain an OSDCloud cache path + and returns only USB, DriveRoot, VolumeLabel, and VolumeUniqueId properties. + + .PARAMETER Include + Optional list of drive letters to include when searching for OSDCloud cache + paths. Accepts values such as 'C', 'D:', or 'E:\'. + + When omitted, all mounted file system drive letters are considered. + + .PARAMETER Exclude + Optional list of drive letters to exclude when searching for OSDCloud cache + paths. Accepts values such as 'C', 'D:', or 'E:\'. + + Excluded drives are skipped even when they are also present in Include. + + .OUTPUTS + System.Object[]. Objects with USB, DriveRoot, VolumeLabel, and VolumeUniqueId. + + .EXAMPLE + Get-OSDCoreCacheDrive + + Returns OSDCloud cache drive metadata for all mounted file system drives. + + .EXAMPLE + Get-OSDCoreCacheDrive -Include C,D -Exclude D + + Returns OSDCloud cache drive metadata only for drive C. + + .LINK + https://github.com/OSDeploy/OSD/tree/master/docs + + .NOTES + Author: David Segura - Recast Software + 2026-07-18 - Initial function created + #> + [CmdletBinding()] + [OutputType([System.Object[]])] + param ( + [Parameter()] + [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] + [string[]]$Include, + + [Parameter()] + [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] + [string[]]$Exclude + ) + + function Get-DriveVolumeMetadata { + param( + [Parameter(Mandatory)] + [string]$DriveRoot + ) + + $volume = $null + $usb = $false + if ($DriveRoot -match '^[A-Z]:\\$') { + try { + $driveLetter = $DriveRoot.Substring(0, 1) + $volume = Get-Volume -DriveLetter $driveLetter -ErrorAction Stop + $usb = [bool](Get-Disk -ErrorAction SilentlyContinue | + Where-Object { $_.BusType -eq 'USB' } | + Get-Partition -ErrorAction SilentlyContinue | + Where-Object { $_.DriveLetter -eq $driveLetter }) + } catch { + $volume = $null + $usb = $false + } + } + + [PSCustomObject]@{ + USB = [bool]$usb + DriveRoot = $DriveRoot + VolumeLabel = if ($volume) { [string]$volume.FileSystemLabel } else { $null } + VolumeUniqueId = if ($volume) { [string]$volume.UniqueId } else { $null } + } + } + + function ConvertTo-DriveLetterList { + param( + [Parameter()] + [string[]]$DriveLetters + ) + + $normalized = foreach ($driveLetter in $DriveLetters) { + if ([string]::IsNullOrWhiteSpace($driveLetter)) { + continue + } + + $value = $driveLetter.Trim().TrimEnd('\\') + if ($value.Length -lt 1) { + continue + } + + $letter = $value.Substring(0, 1).ToUpperInvariant() + if ($letter -match '^[A-Z]$') { + $letter + } + } + + @($normalized | Sort-Object -Unique) + } + + $includedDriveLetters = ConvertTo-DriveLetterList -DriveLetters $Include + $excludedDriveLetters = ConvertTo-DriveLetterList -DriveLetters $Exclude + + $result = Get-PSDrive -PSProvider FileSystem | + Where-Object { $_.Root -match '^[A-Z]:\\$' } | + Where-Object { + $driveLetter = ([string]$_.Root).Substring(0, 1).ToUpperInvariant() + (($includedDriveLetters.Count -eq 0) -or ($driveLetter -in $includedDriveLetters)) -and + ($driveLetter -notin $excludedDriveLetters) + } | + ForEach-Object { + $driveRoot = [string]$_.Root + $osdCloudPath = Join-Path -Path $driveRoot -ChildPath 'OSDCloud' + + if (Test-Path -LiteralPath $osdCloudPath) { + Get-DriveVolumeMetadata -DriveRoot $driveRoot + } + } + + return @($result | Sort-Object -Property DriveRoot -Unique) +} diff --git a/OSDCloud/private/core-device/Get-OSDCoreCacheUSBPath.ps1 b/OSDCloud/private/core-device/Get-OSDCoreCacheUSBPath.ps1 new file mode 100644 index 0000000..813cb43 --- /dev/null +++ b/OSDCloud/private/core-device/Get-OSDCoreCacheUSBPath.ps1 @@ -0,0 +1,98 @@ +function Get-OSDCoreCacheUSBPath { + <# + .SYNOPSIS + Returns OSDCloud cache paths located on USB drives. + + .DESCRIPTION + Uses Get-OSDCoreCacheDrive to enumerate OSDCloud cache drives and returns + the OSDCloud directory path for each discovered cache drive where USB is true, + the file system is NTFS or exFAT, and more than the specified free space is available. + + .PARAMETER Include + Optional list of drive letters to include when searching for OSDCloud cache + drives. Accepts values such as 'C', 'D:', or 'E:\'. + + When omitted, all mounted file system drive letters are considered. + + .PARAMETER Exclude + Optional list of drive letters to exclude when searching for OSDCloud cache + drives. Accepts values such as 'C', 'D:', or 'E:\'. + + Excluded drives are skipped even when they are also present in Include. + + .PARAMETER SizeRemaining + Optional minimum free space required on the USB cache drive, in GB. + + The default is 10 GB. + + .OUTPUTS + System.String[]. OSDCloud directory paths located on USB drives with more + than the specified GB free and an NTFS or exFAT file system. + + .EXAMPLE + Get-OSDCoreCacheUSBPath + + Returns OSDCloud directory paths for all discovered USB cache drives with more than 10 GB free and an NTFS or exFAT file system. + + .EXAMPLE + Get-OSDCoreCacheUSBPath -Include D + + Returns the OSDCloud directory path when drive D contains an OSDCloud cache path, is a USB drive, has more than 10 GB free, and is formatted NTFS or exFAT. + + .EXAMPLE + Get-OSDCoreCacheUSBPath -SizeRemaining 20 + + Returns OSDCloud directory paths for discovered USB cache drives with more than 20 GB free and an NTFS or exFAT file system. + + .LINK + https://github.com/OSDeploy/OSD/tree/master/docs + + .NOTES + Author: David Segura - Recast Software + 2026-07-18 - Initial function created + #> + [CmdletBinding()] + [OutputType([System.String[]])] + param ( + [Parameter()] + [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] + [string[]]$Include, + + [Parameter()] + [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] + [string[]]$Exclude, + + [Parameter()] + [ValidateRange(0, [int]::MaxValue)] + [int]$SizeRemaining = 10 + ) + + $cacheDriveParameters = @{} + if ($PSBoundParameters.ContainsKey('Include')) { + $cacheDriveParameters.Include = $Include + } + if ($PSBoundParameters.ContainsKey('Exclude')) { + $cacheDriveParameters.Exclude = $Exclude + } + + $minimumSizeRemaining = [int64]$SizeRemaining * 1GB + + $cacheUsb = Get-OSDCoreCacheDrive @cacheDriveParameters | + Where-Object { + $volume = $null + if ($_.USB -and $_.DriveRoot -match '^([A-Z]):\\$') { + try { + $volume = Get-Volume -DriveLetter $Matches[1] -ErrorAction Stop + } catch { + $volume = $null + } + } + + $volume -and + ($volume.FileSystem -in @('NTFS', 'exFAT')) -and + ([int64]$volume.SizeRemaining -gt $minimumSizeRemaining) + } | + ForEach-Object { Join-Path -Path $_.DriveRoot -ChildPath 'OSDCloud' } + + return @($cacheUsb | Sort-Object -Unique) +} diff --git a/OSDCloud/private/Initialize-OSDCloudDevice.ps1 b/OSDCloud/private/core-device/Initialize-OSDCoreDevice.ps1 similarity index 85% rename from OSDCloud/private/Initialize-OSDCloudDevice.ps1 rename to OSDCloud/private/core-device/Initialize-OSDCoreDevice.ps1 index fc14f4e..5aa0401 100644 --- a/OSDCloud/private/Initialize-OSDCloudDevice.ps1 +++ b/OSDCloud/private/core-device/Initialize-OSDCoreDevice.ps1 @@ -1,53 +1,40 @@ -<# -.SYNOPSIS -Collects local hardware, firmware, TPM, and network details for OSDCloud. - -.DESCRIPTION -Initialize-OSDCloudDevice gathers device information from CIM classes, firmware, -and environment data, then normalizes manufacturer/model/product values for -workflow use. It writes diagnostic logs to $env:TEMP\osdcloud-logs, attempts to -copy logs to an available OSDCloudLogs path, and populates -$global:OSDCloudDevice with an ordered property set used by downstream OSDCloud -deployment logic. - -.EXAMPLE -Initialize-OSDCloudDevice - -Collects current device metadata, creates or updates -$global:OSDCloudDevice, and writes log artifacts for troubleshooting. - -.OUTPUTS -None. This function does not emit pipeline output. - -.NOTES -Side effects: -- Clears the current PowerShell error collection. -- Updates date/time in WinPE when needed. -- Writes logs to $env:TEMP\osdcloud-logs. -- Sets $global:OSDCloudDevice. -#> -function Initialize-OSDCloudDevice { +function Initialize-OSDCoreDevice { + <# + .SYNOPSIS + Collects local hardware, firmware, TPM, and network details for OSDCloud. + + .DESCRIPTION + Initialize-OSDCoreDevice gathers device information from CIM classes, firmware, + and environment data, then normalizes manufacturer/model/product values for + workflow use. It writes diagnostic logs to $env:TEMP\osdcloud-logs, attempts to + copy logs to an available OSDCloudLogs path, and populates + $global:OSDCoreDevice with an ordered property set used by downstream OSDCloud + deployment logic. + + .EXAMPLE + Initialize-OSDCoreDevice + + Collects current device metadata, creates or updates + $global:OSDCoreDevice, and writes log artifacts for troubleshooting. + + .OUTPUTS + None. This function does not emit pipeline output. + + .NOTES + Side effects: + - Clears the current PowerShell error collection. + - Updates date/time in WinPE when needed. + - Writes logs to $env:TEMP\osdcloud-logs. + - Sets $global:OSDCoreDevice. + #> [CmdletBinding()] param () - function ConvertTo-TrimmedString { - param( - [Parameter(ValueFromPipeline = $true)] - $Value - ) - - process { - if ($null -eq $Value) { - return $null - } - return $Value.ToString().Trim() - } - } #================================================= $Error.Clear() Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)]" #================================================= try { - Sync-OSDCloudDateTime -ThresholdMinutes 5 -Force -ErrorAction Stop + Sync-OSDCoreDateTime -ThresholdMinutes 5 -Force -ErrorAction Stop } catch { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to sync date/time: $($_.Exception.Message)" @@ -59,6 +46,9 @@ function Initialize-OSDCloudDevice { New-Item -Path $LogsPath -ItemType Directory -Force | Out-Null } #================================================= + # Real Architecture + $ProcessorArchitecture = $env:PROCESSOR_ARCHITECTURE + #================================================= # ipconfig ipconfig | Out-File (Join-Path -Path $LogsPath -ChildPath 'Network_IPConfig.txt') -Width 4096 -Force #================================================= @@ -113,8 +103,30 @@ function Initialize-OSDCloudDevice { try { $classWin32Keyboard = Get-CimInstance -ClassName Win32_Keyboard -ErrorAction Stop | Select-Object -Property * $classWin32Keyboard | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_Keyboard.txt') -Width 4096 -Force - $KeyboardLayout = [System.String]$classWin32Keyboard.Layout - $KeyboardName = [System.String]$classWin32Keyboard.Name + + # A device can expose multiple keyboard endpoints. Prefer entries with + # a populated layout and stable ordering, then use the first candidate. + $keyboardCandidates = @($classWin32Keyboard) | Where-Object { + -not [string]::IsNullOrWhiteSpace($_.Layout) -or -not [string]::IsNullOrWhiteSpace($_.Name) + } | Sort-Object -Property @( + @{ Expression = { [string]::IsNullOrWhiteSpace($_.Layout) } ; Descending = $false }, + @{ Expression = { [string]$_.Name } ; Descending = $false }, + @{ Expression = { [string]$_.DeviceID } ; Descending = $false } + ) + + $primaryKeyboard = $keyboardCandidates | Select-Object -First 1 + if ($primaryKeyboard) { + $KeyboardLayout = [System.String]$primaryKeyboard.Layout + $KeyboardName = [System.String]$primaryKeyboard.Name + + if ($keyboardCandidates.Count -gt 1) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Multiple keyboards detected ($($keyboardCandidates.Count)). Using '$KeyboardName' with layout '$KeyboardLayout'." + } + } + else { + $KeyboardLayout = $null + $KeyboardName = $null + } } catch { $classWin32Keyboard = $null @@ -237,7 +249,6 @@ function Initialize-OSDCloudDevice { #================================================= # Secure Boot Information # https://github.com/richardhicks/uefi/blob/main/Get-UEFICertificate.ps1 - try { $SecureBootStatus = Confirm-SecureBootUEFI } @@ -502,6 +513,49 @@ function Initialize-OSDCloudDevice { $OSDProduct = 'Unknown' } #================================================= + # Disk Information + # Include only USB disks that are online and available. + $GetDisk = Get-Disk | + Where-Object { + $_.BusType -eq 'USB' -and + $_.IsOffline -eq $false -and + $_.OperationalStatus -eq 'Online' + } | + Sort-Object DiskNumber | + Select-Object -Property * + + $usbDiskNumbers = [System.Collections.Generic.HashSet[int]]::new() + foreach ($disk in $GetDisk) { + [void]$usbDiskNumbers.Add([int]$disk.DiskNumber) + } + + # Partition Information + $GetPartition = Get-Partition | + Sort-Object DiskNumber, PartitionNumber | + Select-Object -Property *, @{ + Name = 'IsUSB' + Expression = { $usbDiskNumbers.Contains([int]$_.DiskNumber) } + } + # USB Partitions + $USBPartitions = $GetPartition | Where-Object { $_.IsUSB -eq $true } + + # USBVolumes + $usbDriveLetters = $USBPartitions | + ForEach-Object { $_.AccessPaths } | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | + ForEach-Object { + if ($_ -match '^(?[A-Z]):\\$') { + $Matches.DriveLetter + } + } | + Sort-Object -Unique + + $USBVolumes = @() + if ($usbDriveLetters) { + $USBVolumes = Get-Volume -DriveLetter $usbDriveLetters -ErrorAction SilentlyContinue | + Sort-Object DriveLetter -Unique + } + #================================================= # OSDCloudEnv #================================================= # Use OSDCloudEnv to override these properties: @@ -509,7 +563,6 @@ function Initialize-OSDCloudDevice { # OSDModel # OSDProduct # OSArchitecture - $ProcessorArchitecture = $env:PROCESSOR_ARCHITECTURE if ($global:OSDCloudEnv) { if ($global:OSDCloudEnv.OSDManufacturer) { $OSDManufacturer = $global:OSDCloudEnv.OSDManufacturer @@ -528,10 +581,10 @@ function Initialize-OSDCloudDevice { } } #================================================= - # Pass Variables to OSDCloudDevice + # Pass Variables to OSDCoreDevice #================================================= - $global:OSDCloudDevice = $null - $global:OSDCloudDevice = [ordered]@{ + $global:OSDCoreDevice = $null + $global:OSDCoreDevice = [ordered]@{ OSDManufacturer = [System.String]$OSDManufacturer OSDModel = [System.String]$OSDModel OSDProduct = [System.String]$OSDProduct @@ -574,9 +627,22 @@ function Initialize-OSDCloudDevice { TpmManufacturerIdTxt = $DeviceTpmManufacturerIdTxt TpmManufacturerVersion = $DeviceTpmManufacturerVersion TpmSpecVersion = $DeviceTpmSpecVersion + USBPartitions = $USBPartitions + USBVolumes = $USBVolumes UUID = $classWin32ComputerSystemProduct.UUID } - $global:OSDCloudDevice | ConvertTo-Json -Depth 10 | Out-File "$LogsPath\OSDCloudDevice.json" -Force -Encoding utf8 + $global:OSDCoreDevice | ConvertTo-Json -Depth 10 | Out-File "$LogsPath\OSDCoreDevice.json" -Force -Encoding utf8 + #================================================= + # OSDCoreCacheContent + $global:OSDCoreCacheContent = Get-OSDCoreCacheContent + #================================================= + # OSDCoreOperatingSystems + $global:OSDCoreOperatingSystems = Get-OSDCoreOperatingSystems | Where-Object { $_.Architecture -match "$ProcessorArchitecture" } + $null = Set-OSDCoreOperatingSystemCloudObject -OSArchitecture $ProcessorArchitecture + #================================================= + # OSDCoreDriverPacks + $global:OSDCoreDriverPacks = Get-ModuleCoreDriverPacks -OSDManufacturer $OSDManufacturer + $global:OSDCoreDriverPackCloudObject = $global:OSDCoreDriverPacks | Where-Object { $_.SystemId -match $OSDProduct } | Select-Object -First 1 #================================================= # OSDCloudLogs # Look for available drives (USB, mapped network drives, and local drives) with at least 1 GB of free space and write permissions for the current user to copy logs. @@ -616,7 +682,6 @@ function Initialize-OSDCloudDevice { } } #================================================= - $Message = "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" - Write-Verbose -Message $Message; Write-Debug -Message $Message + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" #================================================= } diff --git a/OSDCloud/private/core/Sync-OSDCloudDateTime.ps1 b/OSDCloud/private/core-device/Sync-OSDCoreDateTime.ps1 similarity index 97% rename from OSDCloud/private/core/Sync-OSDCloudDateTime.ps1 rename to OSDCloud/private/core-device/Sync-OSDCoreDateTime.ps1 index 63d82b6..617ea2c 100644 --- a/OSDCloud/private/core/Sync-OSDCloudDateTime.ps1 +++ b/OSDCloud/private/core-device/Sync-OSDCoreDateTime.ps1 @@ -1,4 +1,4 @@ -function Sync-OSDCloudDateTime { +function Sync-OSDCoreDateTime { <# .SYNOPSIS Synchronizes the system clock with internet time from Google. @@ -24,17 +24,17 @@ function Sync-OSDCloudDateTime { time difference, and whether the clock was updated. .EXAMPLE - Sync-OSDCloudDateTime + Sync-OSDCoreDateTime Checks the system time against internet time and reports any differences (in WinPE environment). .EXAMPLE - Sync-OSDCloudDateTime -Force + Sync-OSDCoreDateTime -Force Checks and actually updates the system clock if the time difference exceeds the threshold. .EXAMPLE - Sync-OSDCloudDateTime -ThresholdMinutes 30 -Force -PassThru + Sync-OSDCoreDateTime -ThresholdMinutes 30 -Force -PassThru Uses a 30-minute threshold, updates the clock if needed, and returns detailed synchronization results. diff --git a/OSDCloud/private/core-device/Test-OSDCoreCacheUSB.ps1 b/OSDCloud/private/core-device/Test-OSDCoreCacheUSB.ps1 new file mode 100644 index 0000000..a8123cc --- /dev/null +++ b/OSDCloud/private/core-device/Test-OSDCoreCacheUSB.ps1 @@ -0,0 +1,57 @@ +function Test-OSDCoreCacheUSB { + <# + .SYNOPSIS + Tests whether any OSDCloud cache drive is a USB drive. + + .DESCRIPTION + Uses Get-OSDCoreCacheDrive to enumerate OSDCloud cache drives and returns + true when at least one discovered cache drive has USB set to true. + + .PARAMETER Include + Optional list of drive letters to include when searching for OSDCloud cache + drives. Accepts values such as 'C', 'D:', or 'E:\'. + + When omitted, all mounted file system drive letters are considered. + + .PARAMETER Exclude + Optional list of drive letters to exclude when searching for OSDCloud cache + drives. Accepts values such as 'C', 'D:', or 'E:\'. + + Excluded drives are skipped even when they are also present in Include. + + .OUTPUTS + System.Boolean. True when an OSDCloud cache drive is on USB; otherwise false. + + .EXAMPLE + Test-OSDCoreCacheUSB + + Returns true if any discovered OSDCloud cache drive is a USB drive. + + .EXAMPLE + Test-OSDCoreCacheUSB -Include D + + Returns true if drive D contains an OSDCloud cache path and is a USB drive. + + .LINK + https://github.com/OSDeploy/OSD/tree/master/docs + + .NOTES + Author: David Segura - Recast Software + 2026-07-18 - Initial function created + #> + [CmdletBinding()] + [OutputType([System.Boolean])] + param ( + [Parameter()] + [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] + [string[]]$Include, + + [Parameter()] + [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] + [string[]]$Exclude + ) + + $cacheDrive = @(Get-OSDCoreCacheDrive @PSBoundParameters | Where-Object { $_.USB }) + + return [bool]($cacheDrive.Count -gt 0) +} diff --git a/OSDCloud/private/core-device/Update-RecastOSDCloudUSBCache.ps1 b/OSDCloud/private/core-device/Update-RecastOSDCloudUSBCache.ps1 new file mode 100644 index 0000000..2a08512 --- /dev/null +++ b/OSDCloud/private/core-device/Update-RecastOSDCloudUSBCache.ps1 @@ -0,0 +1,452 @@ +function Update-RecastOSDCloudUSBCache { + <# + .SYNOPSIS + Starts the Recast OSDCloud command-line deployment workflow. + + .DESCRIPTION + Initializes device and deployment context, discovers matching operating systems, + resolves driver pack metadata for the current device (or supplied overrides), + validates required dependencies, and prepares global state consumed by + the Recast OSDCloud CLI workflow. + + .PARAMETER OSArchitecture + Operating system architecture used when selecting catalog entries. + Supported values are amd64 and arm64. + + .PARAMETER OSReleaseID + Operating system release identifier used for catalog selection. + + .PARAMETER OSLanguageCode + Operating system language code used for catalog selection. + If not specified, the value is inferred from the current keyboard layout. + + .PARAMETER OSActivation + Operating system activation channel used for catalog selection. + + .PARAMETER OSEdition + Operating system edition used for catalog selection. + Valid values depend on OSArchitecture at runtime. + + .PARAMETER OSDManufacturer + Overrides the detected computer manufacturer for driver pack matching. + If omitted, the detected device manufacturer is used. + + .PARAMETER OSDModel + Overrides the detected computer model for logging and context alignment. + If omitted, the detected device model is used. + + .PARAMETER OSDProduct + Overrides the detected computer product/system ID for driver pack matching. + If omitted, the detected device product value is used. + + .PARAMETER WinPEPostAction + Specifies the action to take after the WinPE deployment workflow completes. + + .EXAMPLE + Update-RecastOSDCloudUSBCache + Updates the Recast OSDCloud USB cache using detected device values and default deployment selection. + + .EXAMPLE + Update-RecastOSDCloudUSBCache -OSArchitecture arm64 -OSEdition Pro -OSReleaseID 24H2 + Updates the Recast OSDCloud USB cache for an ARM64 Windows 11 Pro 24H2 deployment selection. + + .LINK + https://github.com/OSDeploy/OSD/tree/master/docs + + .NOTES + Author: David Segura - Recast Software + 2026-07-09 - Standardized comment-based help metadata and links. + 2026-07-14 - Updated help content for CLI-specific behavior and parameter documentation. + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false, HelpMessage = 'Operating system architecture for deployment selection.')] + [ValidateNotNullOrEmpty()] + [ValidateSet('amd64','arm64')] + [string] + $OSArchitecture = $env:PROCESSOR_ARCHITECTURE, + + [Parameter(Mandatory = $false, HelpMessage = 'Operating system release identifier for deployment selection.')] + [ValidateNotNullOrEmpty()] + [ValidateSet('25H2','24H2','23H2','22H2','21H2')] + [string] + $OSReleaseID = '25H2', + + [Parameter(Mandatory = $false, HelpMessage = 'Operating system language code for deployment selection.')] + [ValidateNotNullOrEmpty()] + [ValidateSet ( + 'ar-sa','bg-bg','cs-cz','da-dk','de-de','el-gr', + 'en-gb','en-us','es-es','es-mx','et-ee','fi-fi', + 'fr-ca','fr-fr','he-il','hr-hr','hu-hu','it-it', + 'ja-jp','ko-kr','lt-lt','lv-lv','nb-no','nl-nl', + 'pl-pl','pt-br','pt-pt','ro-ro','ru-ru','sk-sk', + 'sl-si','sr-latn-rs','sv-se','th-th','tr-tr', + 'uk-ua','zh-cn','zh-tw' + )] + [string] + $OSLanguageCode, + + [Parameter(Mandatory = $false, HelpMessage = 'Operating system activation channel for deployment selection.')] + [ValidateNotNullOrEmpty()] + [ValidateSet('Retail','Volume')] + [string] + $OSActivation = 'Retail', + + [Parameter(Mandatory = $false, HelpMessage = 'Operating system edition identifier for deployment selection.')] + [ValidateNotNullOrEmpty()] + [string] + $OSEdition = 'Pro', + + [Parameter(Mandatory = $false, HelpMessage = 'Optional manufacturer override used for driver pack selection.')] + [ValidateNotNullOrEmpty()] + [string] + $OSDManufacturer, + + [Parameter(Mandatory = $false, HelpMessage = 'Optional model override used for driver pack selection.')] + [ValidateNotNullOrEmpty()] + [string] + $OSDModel, + + [Parameter(Mandatory = $false, HelpMessage = 'Optional product/system ID override used for driver pack selection.')] + [ValidateNotNullOrEmpty()] + [string] + $OSDProduct, + + [Parameter(Mandatory = $false, HelpMessage = 'WinPE Post Action.')] + [ValidateNotNullOrEmpty()] + [ValidateSet('Quit','Restart','Shutdown')] + [string] + $WinPEPostAction = 'Quit' + ) + #================================================= + $OSDCoreLicense = Get-OSDCoreLicense + if (-not $OSDCoreLicense -or $OSDCoreLicense.IsValid -ne $true) { + # Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] A valid Recast Core license is required." + Show-OSDCoreLicenseHelp + return + } + #================================================= + # Emit function/version context and surface legacy parameter usage. + $ModuleVersion = $($MyInvocation.MyCommand.Module.Version) + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] $ModuleVersion" + #================================================= + # Dependency guard: OSDCloud relies on curl.exe for downloads. + if (-not (Get-Command -Name 'curl.exe' -ErrorAction SilentlyContinue)) { + throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDCloud requires 'curl.exe' which is not available on this system. Please ensure curl.exe is available in the system PATH." + } + #================================================= + # Require an eligible USB cache before doing any online catalog or download work. + $osdCoreCacheUsbPath = Get-OSDCoreCacheUSBPath | Select-Object -First 1 + if (-not $osdCoreCacheUsbPath) { + throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] No eligible OSDCoreCache USB drive was detected. Connect a USB drive with an OSDCloud directory, NTFS or exFAT format, and more than 10 GB free." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDCoreCache USB is available at $osdCoreCacheUsbPath" + #================================================= + # Resolve architecture-specific edition constraints and normalize edition metadata. + $OSEditionValuesByArchitecture = @{ + amd64 = @('Home','Home N','Education','Education N','Pro','Pro N','Enterprise','Enterprise N') + arm64 = @('Home','Pro','Enterprise') + } + if (-not $OSEditionValuesByArchitecture.ContainsKey($OSArchitecture)) { + throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unsupported OSArchitecture '$OSArchitecture'." + } + + $OSEditionValues = $OSEditionValuesByArchitecture[$OSArchitecture] + if ($OSEdition -notin $OSEditionValues) { + throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSEdition '$OSEdition' is not valid for OSArchitecture '$OSArchitecture'. Valid values: $($OSEditionValues -join ', ')." + } + + $OSEditionIdByName = @{ + 'Home' = 'Core' + 'Home N' = 'CoreN' + 'Education' = 'Education' + 'Education N' = 'EducationN' + 'Pro' = 'Professional' + 'Pro N' = 'ProfessionalN' + 'Enterprise' = 'Enterprise' + 'Enterprise N' = 'EnterpriseN' + } + $OSEditionId = $OSEditionIdByName[$OSEdition] + if ([string]::IsNullOrWhiteSpace($OSEditionId)) { + throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to resolve OSEditionId for OSEdition '$OSEdition'." + } + Write-Verbose -Message ('[{0}] [{1}] OSEditionId: {2}' -f (Get-Date -format s), $MyInvocation.MyCommand.Name, $OSEditionId) + #================================================= + # OSDCoreDevice + if (-not ($global:OSDCoreDevice)) { + Initialize-OSDCoreDevice + } + #================================================= + # OSDCoreOperatingSystems + if ($PSBoundParameters.ContainsKey('OSArchitecture')) { + $global:OSDCoreOperatingSystems = Get-OSDCoreOperatingSystems | Where-Object { $_.Architecture -match "$OSArchitecture" } + } + + # Validate that the OS catalog was preloaded for this architecture. + if (-not $global:OSDCoreOperatingSystems) { + throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to load OperatingSystem Catalog" + } + + # Automatically determine default OSLanguageCode from the detected keyboard layout if not explicitly provided. + if (-not $PSBoundParameters.ContainsKey('OSLanguageCode')) { + if (Get-Command -Name 'Convert-KeyboardLayoutToLanguageCode' -ErrorAction SilentlyContinue) { + $OSLanguageCode = Convert-KeyboardLayoutToLanguageCode -KeyboardLayout $global:OSDCoreDevice.KeyboardLayout -FallbackLanguageCode 'en-US' + } + else { + $OSLanguageCode = 'en-US' + } + } + + # Select and set the matching operating system object using core selection logic. + $global:OSDCoreOperatingSystemCloudObject = Set-OSDCoreOperatingSystemCloudObject ` + -OSActivation $OSActivation ` + -OSArchitecture $OSArchitecture ` + -OSLanguageCode $OSLanguageCode ` + -OSReleaseID $OSReleaseID ` + -OSVersion 'Windows 11' + + if (-not $global:OSDCoreOperatingSystemCloudObject) { + throw "[$(Get-Date -format s)] Unable to find a matching operating system object for OSReleaseID '$OSReleaseID', OSArchitecture '$OSArchitecture', Activation '$OSActivation', and Language '$OSLanguageCode'." + } + #================================================= + # OSDCoreDriverPacks + # Resolve driver pack metadata for the detected device, with optional manufacturer overrides supplied by the caller. + if ($PSBoundParameters.ContainsKey('OSDManufacturer')) { + $global:OSDCoreDevice.OSDManufacturer = $OSDManufacturer + $global:OSDCoreDriverPacks = Get-ModuleCoreDriverPacks -OSDManufacturer $OSDManufacturer + } + + # Resolve driver pack metadata for the detected device, with optional model overrides supplied by the caller. + if ($PSBoundParameters.ContainsKey('OSDModel')) { + $global:OSDCoreDevice.OSDModel = $OSDModel + } + + # Resolve driver pack metadata for the detected device, with optional product overrides supplied by the caller. + if ($PSBoundParameters.ContainsKey('OSDProduct')) { + $global:OSDCoreDevice.OSDProduct = $OSDProduct + } + $global:OSDCoreDriverPackCloudObject = $global:OSDCoreDriverPacks | Where-Object { $_.SystemId -match $global:OSDCoreDevice.OSDProduct } | Select-Object -First 1 + #================================================ + # OSDCoreOperatingSystemCloudObject + if ($global:OSDCoreOperatingSystemCloudObject) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Verifying OSDCoreOperatingSystemCloudObject." + # Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] OSDCoreOperatingSystemCloudObject:" + $tempOSDCoreOperatingSystemCloudObject = $global:OSDCoreOperatingSystemCloudObject | Select-Object -Property Name, FileName, Url, SHA1, SHA256 + # $global:OSDCoreOperatingSystemCloudObject | Out-Host + $tempOSDCoreOperatingSystemCloudObject | Out-Host + + # Confirm the selected operating system download URL before offering cache download work. + $OSDCoreOperatingSystemCloudObjectUrlReachable = Test-OSDCoreOperatingSystemCloudObject -OperatingSystemCloudObject $global:OSDCoreOperatingSystemCloudObject + if ($OSDCoreOperatingSystemCloudObjectUrlReachable) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OperatingSystem URL is reachable. OK." + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] OperatingSystem URL is not reachable." + } + + # Prefer SHA256 when the catalog provides it, and fall back to SHA1 for older entries. + $expectedOperatingSystemHash = $null + $expectedOperatingSystemHashAlgorithm = $null + if (-not [string]::IsNullOrWhiteSpace([string]$global:OSDCoreOperatingSystemCloudObject.SHA256)) { + $expectedOperatingSystemHash = [string]$global:OSDCoreOperatingSystemCloudObject.SHA256 + $expectedOperatingSystemHashAlgorithm = 'SHA256' + } + elseif (-not [string]::IsNullOrWhiteSpace([string]$global:OSDCoreOperatingSystemCloudObject.SHA1)) { + $expectedOperatingSystemHash = [string]$global:OSDCoreOperatingSystemCloudObject.SHA1 + $expectedOperatingSystemHashAlgorithm = 'SHA1' + } + + # Check whether the selected OS payload is already present in the USB cache inventory. + $osdCoreOperatingSystemCacheContent = Get-OSDCoreOperatingSystemCacheObject -OperatingSystemCloudObject $global:OSDCoreOperatingSystemCloudObject + if ($osdCoreOperatingSystemCacheContent) { + # Verify the cached payload before treating it as ready. + if (-not [string]::IsNullOrWhiteSpace($expectedOperatingSystemHash)) { + $actualOperatingSystemHash = (Get-FileHash -Path $osdCoreOperatingSystemCacheContent.FullName -Algorithm $expectedOperatingSystemHashAlgorithm -ErrorAction Stop).Hash + if ($actualOperatingSystemHash -ne $expectedOperatingSystemHash.Trim()) { + throw "[$(Get-Date -format s)] OSDCoreOperatingSystemCloudObject $expectedOperatingSystemHashAlgorithm hash mismatch for $($osdCoreOperatingSystemCacheContent.FullName). Expected $($expectedOperatingSystemHash.Trim()), found $actualOperatingSystemHash." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OperatingSystem cached file $expectedOperatingSystemHashAlgorithm hash verified. OK." + } + else { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OperatingSystem cached file hash was not verified because no hash property was available." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OperatingSystem is ready at $($osdCoreOperatingSystemCacheContent.FullName)." + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] OperatingSystem is not available on a USB drive." + + # Do not offer a download when the catalog URL cannot be reached. + if (-not $OSDCoreOperatingSystemCloudObjectUrlReachable) { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] OperatingSystem download was not offered because the URL is not reachable." + } + elseif (Test-OSDCoreCacheUSB) { + $osdCoreCacheUsbPath = Get-OSDCoreCacheUSBPath | Select-Object -First 1 + if ($osdCoreCacheUsbPath) { + # Build the destination path used by the USB cache OS folder layout. + $osdCoreOperatingSystemDestinationChildPath = "$($global:OSDCoreOperatingSystemCloudObject.Version) $($global:OSDCoreOperatingSystemCloudObject.ReleaseID)" + $osdCoreOperatingSystemDestination = [System.IO.Path]::GetFullPath((Join-Path -Path (Join-Path -Path ([string]$osdCoreCacheUsbPath) -ChildPath 'OS') -ChildPath $osdCoreOperatingSystemDestinationChildPath)) + $osdCoreOperatingSystemDestinationFullName = Join-Path -Path $osdCoreOperatingSystemDestination -ChildPath ([string]$global:OSDCoreOperatingSystemCloudObject.FileName) + $downloadOperatingSystem = $true + + # If the destination file already exists and verifies, skip the interactive download prompt. + if (Test-Path -LiteralPath $osdCoreOperatingSystemDestinationFullName) { + if (-not [string]::IsNullOrWhiteSpace($expectedOperatingSystemHash)) { + $actualOperatingSystemHash = (Get-FileHash -Path $osdCoreOperatingSystemDestinationFullName -Algorithm $expectedOperatingSystemHashAlgorithm -ErrorAction Stop).Hash + if ($actualOperatingSystemHash -ne $expectedOperatingSystemHash.Trim()) { + throw "[$(Get-Date -format s)] OperatingSystem $expectedOperatingSystemHashAlgorithm hash mismatch for $osdCoreOperatingSystemDestinationFullName. Expected $($expectedOperatingSystemHash.Trim()), found $actualOperatingSystemHash." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OperatingSystem existing file $expectedOperatingSystemHashAlgorithm hash verified. OK." + } + else { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OperatingSystem already exists at $osdCoreOperatingSystemDestinationFullName. No hash property was available to verify." + } + $global:OSDCoreCacheContent = Get-OSDCoreCacheContent + $downloadOperatingSystem = $false + } + + # Prompt before downloading the OS payload because it can be large. + $caption = 'Download OperatingSystem to OSDCoreCache USB' + $message = "Download $($global:OSDCoreOperatingSystemCloudObject.FileName) to: $osdCoreOperatingSystemDestination" + $choices = @( + (New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Download the OperatingSystem to the USB cache.'), + (New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Skip the OperatingSystem download.') + ) + if ($downloadOperatingSystem -and ($host.UI.PromptForChoice($caption, $message, $choices, 1) -eq 0)) { + $savedOperatingSystem = Invoke-OSDCoreDownloadFile -SourceUrl $global:OSDCoreOperatingSystemCloudObject.Url -DestinationDirectory $osdCoreOperatingSystemDestination -DestinationName $global:OSDCoreOperatingSystemCloudObject.FileName -ErrorAction Stop + + # Verify the downloaded payload before refreshing the cache inventory. + if (-not [string]::IsNullOrWhiteSpace($expectedOperatingSystemHash)) { + $actualOperatingSystemHash = (Get-FileHash -Path $savedOperatingSystem.FullName -Algorithm $expectedOperatingSystemHashAlgorithm -ErrorAction Stop).Hash + if ($actualOperatingSystemHash -ne $expectedOperatingSystemHash.Trim()) { + throw "[$(Get-Date -format s)] OSDCoreOperatingSystemCloudObject $expectedOperatingSystemHashAlgorithm hash mismatch for $($savedOperatingSystem.FullName). Expected $($expectedOperatingSystemHash.Trim()), found $actualOperatingSystemHash." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDCoreOperatingSystemCloudObject $expectedOperatingSystemHashAlgorithm hash verified. OK." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDCoreOperatingSystemCloudObject downloaded to $($savedOperatingSystem.FullName)" + $global:OSDCoreCacheContent = Get-OSDCoreCacheContent + } + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] No eligible OSDCoreCache USB path is available for OperatingSystem download." + } + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] No eligible OSDCoreCache USB drive is available for OperatingSystem download." + } + } + } else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] OSDCoreOperatingSystemCloudObject is not set." + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] OSDCloud will not function on this device or on this network." + } + #================================================ + # OSDCoreDriverPackCloudObject + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDManufacturer: $($global:OSDCoreDevice.OSDManufacturer)" + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDModel: $($global:OSDCoreDevice.OSDModel)" + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDProduct: $($global:OSDCoreDevice.OSDProduct)" + if ($global:OSDCoreDriverPackCloudObject) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Verifying OSDCoreDriverPackCloudObject." + $global:OSDCoreDriverPackCloudObject | Out-Host + + $OSDCoreDriverPackCloudObjectUrlReachable = Test-OSDCoreDriverPackCloudObject -DriverPackCloudObject $global:OSDCoreDriverPackCloudObject + if ($OSDCoreDriverPackCloudObjectUrlReachable) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] DriverPack URL is reachable. OK." + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] DriverPack URL is not reachable." + } + + # Driver pack catalogs use either HashMD5 or MD5Hash depending on the source. + $expectedDriverPackHashMD5 = $null + if ($global:OSDCoreDriverPackCloudObject.PSObject.Properties.Match('HashMD5').Count -gt 0) { + $expectedDriverPackHashMD5 = [string]$global:OSDCoreDriverPackCloudObject.HashMD5 + } + elseif ($global:OSDCoreDriverPackCloudObject.PSObject.Properties.Match('MD5Hash').Count -gt 0) { + $expectedDriverPackHashMD5 = [string]$global:OSDCoreDriverPackCloudObject.MD5Hash + } + + # Check whether the selected driver pack is already present in the cache inventory. + $osdCoreDriverPackCacheContent = Get-OSDCoreDriverPackCacheObject -DriverPackCloudObject $global:OSDCoreDriverPackCloudObject + if ($osdCoreDriverPackCacheContent) { + # Verify cached driver pack integrity when the catalog includes an MD5 hash. + if (-not [string]::IsNullOrWhiteSpace($expectedDriverPackHashMD5)) { + $actualDriverPackHashMD5 = (Get-FileHash -Path $osdCoreDriverPackCacheContent.FullName -Algorithm MD5 -ErrorAction Stop).Hash + if ($actualDriverPackHashMD5 -ne $expectedDriverPackHashMD5.Trim()) { + throw "[$(Get-Date -format s)] DriverPack MD5 hash mismatch for $($osdCoreDriverPackCacheContent.FullName). Expected $($expectedDriverPackHashMD5.Trim()), found $actualDriverPackHashMD5." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] DriverPack cached file MD5 hash verified. OK." + } + else { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] DriverPack cached file hash was not verified because no MD5 hash property was available." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] DriverPack is ready at $($osdCoreDriverPackCacheContent.FullName)" + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] DriverPack is not available on a USB Drive." + + # Do not offer a download when the driver pack URL cannot be reached. + if (-not $OSDCoreDriverPackCloudObjectUrlReachable) { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] DriverPack download was not offered because the URL is not reachable." + } + elseif (Test-OSDCoreCacheUSB) { + $osdCoreCacheUsbPath = Get-OSDCoreCacheUSBPath | Select-Object -First 1 + if ($osdCoreCacheUsbPath) { + # Store driver packs under the cache DriverPacks folder by manufacturer. + $osdCoreDriverPackDestination = [System.IO.Path]::GetFullPath((Join-Path -Path (Join-Path -Path ([string]$osdCoreCacheUsbPath) -ChildPath 'DriverPacks') -ChildPath ([string]$global:OSDCoreDriverPackCloudObject.Manufacturer))) + $osdCoreDriverPackDestinationFullName = Join-Path -Path $osdCoreDriverPackDestination -ChildPath ([string]$global:OSDCoreDriverPackCloudObject.FileName) + $downloadDriverPack = $true + + # If a valid destination file already exists, refresh cache content and skip download. + if (Test-Path -LiteralPath $osdCoreDriverPackDestinationFullName) { + if (-not [string]::IsNullOrWhiteSpace($expectedDriverPackHashMD5)) { + $actualDriverPackHashMD5 = (Get-FileHash -Path $osdCoreDriverPackDestinationFullName -Algorithm MD5 -ErrorAction Stop).Hash + if ($actualDriverPackHashMD5 -ne $expectedDriverPackHashMD5.Trim()) { + throw "[$(Get-Date -format s)] DriverPack MD5 hash mismatch for $osdCoreDriverPackDestinationFullName. Expected $($expectedDriverPackHashMD5.Trim()), found $actualDriverPackHashMD5." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] DriverPack existing file MD5 hash verified. OK." + } + else { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] DriverPack already exists at $osdCoreDriverPackDestinationFullName. No MD5 hash property was available to verify." + } + $global:OSDCoreCacheContent = Get-OSDCoreCacheContent + $downloadDriverPack = $false + } + + # Prompt before downloading because driver packs can be large and vendor-specific. + $caption = 'Download DriverPack to OSDCoreCache USB' + $message = "Download $($global:OSDCoreDriverPackCloudObject.FileName) to: $osdCoreDriverPackDestination" + $choices = @( + (New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Download the driver pack to the USB cache.'), + (New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Skip the driver pack download.') + ) + if ($downloadDriverPack -and ($host.UI.PromptForChoice($caption, $message, $choices, 1) -eq 0)) { + $savedDriverPack = Invoke-OSDCoreDownloadFile -SourceUrl $global:OSDCoreDriverPackCloudObject.Url -DestinationDirectory $osdCoreDriverPackDestination -DestinationName $global:OSDCoreDriverPackCloudObject.FileName -ErrorAction Stop + + # Verify the downloaded driver pack before refreshing the cache inventory. + if (-not [string]::IsNullOrWhiteSpace($expectedDriverPackHashMD5)) { + $actualDriverPackHashMD5 = (Get-FileHash -Path $savedDriverPack.FullName -Algorithm MD5 -ErrorAction Stop).Hash + if ($actualDriverPackHashMD5 -ne $expectedDriverPackHashMD5.Trim()) { + throw "[$(Get-Date -format s)] DriverPack MD5 hash mismatch for $($savedDriverPack.FullName). Expected $($expectedDriverPackHashMD5.Trim()), found $actualDriverPackHashMD5." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] DriverPack MD5 hash verified. OK." + } + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] DriverPack downloaded to $($savedDriverPack.FullName)" + $global:OSDCoreCacheContent = Get-OSDCoreCacheContent + } + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] No eligible OSDCoreCache USB path is available for download." + } + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] No eligible OSDCoreCache USB drive is available for download." + } + } + } + else { + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] OSDCoreDriverPackCloudObject is not set." + Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] OSDCloud will not apply a DriverPack for this device or on this network." + } + #================================================ + Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] Done." + #================================================ +} diff --git a/OSDCloud/private/core-driverpack/Get-ModuleCoreDriverPacks.ps1 b/OSDCloud/private/core-driverpack/Get-ModuleCoreDriverPacks.ps1 new file mode 100644 index 0000000..1973109 --- /dev/null +++ b/OSDCloud/private/core-driverpack/Get-ModuleCoreDriverPacks.ps1 @@ -0,0 +1,65 @@ +function Get-ModuleCoreDriverPacks { + <# + .SYNOPSIS + Retrieves driver pack information for the specified manufacturer and operating system architecture. + + .DESCRIPTION + Gets driver pack catalogs based on the device manufacturer and OS architecture. For AMD64 architecture, + manufacturer-specific catalogs are loaded. For ARM64 and other architectures, the default catalog is returned. + Supports Dell, HP, Lenovo, Microsoft (Surface), and generic devices. + + .PARAMETER OSDManufacturer + The device manufacturer name. Defaults to the value from $global:OSDCoreDevice.OSDManufacturer. + Supported values: Dell, HP, Lenovo, Microsoft, or any other value will use the Default catalog. + + .PARAMETER ProcessorArchitecture + The operating system architecture. Defaults to the value from $global:OSDCoreDevice.ProcessorArchitecture. + Typically 'amd64' or 'arm64'. + + .OUTPUTS + PSCustomObject + Array of driver pack objects containing driver information for the specified manufacturer and architecture. + + .EXAMPLE + PS> Get-ModuleCoreDriverPacks + Returns driver packs for the current device's manufacturer and architecture. + + .EXAMPLE + PS> Get-ModuleCoreDriverPacks -OSDManufacturer 'Dell' -ProcessorArchitecture 'amd64' + Returns driver packs for Dell devices with AMD64 architecture. + + .NOTES + Requires manufacturer-specific cmdlets (Get-OSDCoreDriverPackCatalogDell, Get-OSDCoreDriverPackCatalogHP, etc.) to be available. + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [System.String]$GenericDriverPackJson = (Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) 'core\driverpacks\generic.json'), + + [System.String]$OSDManufacturer = $global:OSDCoreDevice.OSDManufacturer, + + [System.String]$ProcessorArchitecture = $env:PROCESSOR_ARCHITECTURE + ) + + # Load Generic driver pack catalog for fallback + $GenericCatalog = Get-Content -Path $GenericDriverPackJson -Raw | ConvertFrom-Json + + if ($ProcessorArchitecture -eq 'amd64') { + $DriverPackValues = switch ($OSDManufacturer) { + 'Dell' { Get-OSDCoreDriverPackCatalogDell } + 'HP' { Get-OSDCoreDriverPackCatalogHP } + 'Lenovo' { Get-OSDCoreDriverPackCatalogLenovo } + 'Microsoft' { Get-OSDCoreDriverPackCatalogSurface } + default { $GenericCatalog } + } + } + else { + $DriverPackValues = switch ($OSDManufacturer) { + 'Microsoft' { Get-OSDCoreDriverPackCatalogSurface } + default { $GenericCatalog } + } + } + + $DriverPackValues | Where-Object { $_.OSArchitecture -eq $ProcessorArchitecture } +} diff --git a/OSDCloud/private/core/Get-OSDCloudCatalogDell.ps1 b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogDell.ps1 similarity index 75% rename from OSDCloud/private/core/Get-OSDCloudCatalogDell.ps1 rename to OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogDell.ps1 index 90ede3d..0d7e907 100644 --- a/OSDCloud/private/core/Get-OSDCloudCatalogDell.ps1 +++ b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogDell.ps1 @@ -1,4 +1,4 @@ -function Get-OSDCloudCatalogDell { +function Get-OSDCoreDriverPackCatalogDell { <# .SYNOPSIS Downloads and parses the Dell driver pack catalog for Windows 11. @@ -6,13 +6,42 @@ function Get-OSDCloudCatalogDell { .DESCRIPTION Retrieves the latest Dell DriverPackCatalog.cab from Dell's download site, extracts and parses it to create a catalog of available Windows 11 driver packs. - Falls back to offline catalog if download fails. + If online retrieval fails, the function falls back to the bundled local catalog. + + .PARAMETER LocalDriverPackCatalog + Path to the local fallback Dell catalog XML file. This file is used when the + online catalog cannot be downloaded or extracted. + + .PARAMETER OemDriverPackCatalog + URL to the online Dell DriverPack catalog CAB file. + + .PARAMETER Force + Forces download and rebuild of the temporary online catalog even when a + cached temp catalog file already exists. + + .PARAMETER LocalOnly + Uses only local catalog values and skips online catalog download/extraction. .EXAMPLE - Get-OSDCloudCatalogDell + Get-OSDCoreDriverPackCatalogDell Retrieves the Dell driver pack catalog for Windows 11. + .EXAMPLE + Get-OSDCoreDriverPackCatalogDell -Force + + Forces a fresh online download of the Dell catalog before parsing. + + .EXAMPLE + Get-OSDCoreDriverPackCatalogDell -LocalDriverPackCatalog 'C:\Catalogs\dell.xml' + + Uses a custom local fallback catalog path. + + .EXAMPLE + Get-OSDCoreDriverPackCatalogDell -LocalOnly + + Processes only local catalog values without any online download checks. + .OUTPUTS PSCustomObject[] Returns custom objects with driver pack information including Name, Model, @@ -22,22 +51,37 @@ function Get-OSDCloudCatalogDell { Catalog is downloaded from https://downloads.dell.com/catalog/DriverPackCatalog.cab #> [CmdletBinding()] - param () + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [System.String]$LocalDriverPackCatalog = (Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) 'core\driverpacks\dell.xml'), + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [System.String]$OemDriverPackCatalog = 'https://downloads.dell.com/catalog/DriverPackCatalog.cab', + + [Parameter(Mandatory = $false)] + [switch]$Force, + + [Parameter(Mandatory = $false)] + [switch]$LocalOnly + ) begin { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" #================================================= # Catalogs - $localDriverPackCatalog = Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) $OSDCloudModule.dell.driverpackcataloglocal - $oemDriverPackCatalog = $OSDCloudModule.dell.driverpackcatalogoem $tempCatalogPackagePath = "$($env:TEMP)\DriverPackCatalog.cab" $tempCatalogPath = "$($env:TEMP)\osdcloud-driverpack-dell.xml" #================================================= # Build realtime catalog from online source, if fails fallback to offline catalog try { - if ($Force -or -not (Test-Path $tempCatalogPath)) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $oemDriverPackCatalog" - $null = Invoke-WebRequest -Uri $oemDriverPackCatalog -OutFile $tempCatalogPackagePath -ErrorAction Stop + if ($LocalOnly) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] LocalOnly requested; skipping online catalog download" + } + elseif ($Force -or -not (Test-Path $tempCatalogPath)) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $OemDriverPackCatalog" + $null = Invoke-WebRequest -Uri $OemDriverPackCatalog -OutFile $tempCatalogPackagePath -ErrorAction Stop if (Test-Path $tempCatalogPackagePath) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Extracting $tempCatalogPath" @@ -56,12 +100,16 @@ function Get-OSDCloudCatalogDell { } # Load catalog content - if (Test-Path $tempCatalogPath) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $tempCatalogPath" + if ($LocalOnly) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + [xml]$XmlCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw + } + elseif (Test-Path $tempCatalogPath) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $tempCatalogPath" [xml]$XmlCatalogContent = Get-Content -Path $tempCatalogPath -Raw } else { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $localDriverPackCatalog" - [xml]$XmlCatalogContent = Get-Content -Path $localDriverPackCatalog -Raw + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + [xml]$XmlCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw } # Validate catalog content diff --git a/OSDCloud/private/core/Get-OSDCloudCatalogHP.ps1 b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogHP.ps1 similarity index 81% rename from OSDCloud/private/core/Get-OSDCloudCatalogHP.ps1 rename to OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogHP.ps1 index 0155ed2..e90c290 100644 --- a/OSDCloud/private/core/Get-OSDCloudCatalogHP.ps1 +++ b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogHP.ps1 @@ -1,4 +1,4 @@ -function Get-OSDCloudCatalogHp { +function Get-OSDCoreDriverPackCatalogHP { <# .SYNOPSIS Downloads and parses the HP driver pack catalog for Windows 11. @@ -8,20 +8,34 @@ function Get-OSDCloudCatalogHp { extracts and parses it to create a catalog of available Windows 11 driver packs. Falls back to offline catalog if download fails. + .PARAMETER LocalDriverPackCatalog + Path to the local fallback HP catalog XML file. + + .PARAMETER OemDriverPackCatalog + URL to the online HP driver pack catalog CAB file. + + .PARAMETER Force + Forces download and rebuild of the temporary online catalog even when a + cached temp catalog file already exists. + + .PARAMETER LocalOnly + Uses only local catalog values and skips online catalog download/extraction. + .EXAMPLE - Get-OSDCloudCatalogHp + Get-OSDCoreDriverPackCatalogHP Retrieves the HP driver pack catalog for Windows 11. .EXAMPLE - Get-OSDCloudCatalogHp -Force + Get-OSDCoreDriverPackCatalogHP -Force Forces a refresh of the HP driver pack catalog by downloading the latest version from HP's server, bypassing any cached copies. - .PARAMETER Force - If specified, forces a download of the latest catalog from HP's server. - By default, if a cached catalog exists, it will be used. + .EXAMPLE + Get-OSDCoreDriverPackCatalogHP -LocalOnly + + Processes only local catalog values without any online download checks. .OUTPUTS PSCustomObject[] @@ -34,24 +48,36 @@ function Get-OSDCloudCatalogHp { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] - [switch]$Force + [ValidateNotNullOrEmpty()] + [string]$LocalDriverPackCatalog = (Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) 'core\driverpacks\hp.xml'), + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$OemDriverPackCatalog = 'https://hpia.hpcloud.hp.com/downloads/driverpackcatalog/HPClientDriverPackCatalog.cab', + + [Parameter(Mandatory = $false)] + [switch]$Force, + + [Parameter(Mandatory = $false)] + [switch]$LocalOnly ) begin { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" #================================================= # Catalogs - $localDriverPackCatalog = Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) $OSDCloudModule.hp.driverpackcataloglocal - $oemDriverPackCatalog = $OSDCloudModule.hp.driverpackcatalogoem $tempCatalogPackagePath = "$($env:TEMP)\HPClientDriverPackCatalog.cab" $tempCatalogPath = "$($env:TEMP)\osdcloud-driverpack-hp.xml" #================================================= # Build realtime catalog from online source, if fails fallback to offline catalog try { - if ($Force -or -not (Test-Path $tempCatalogPath)) { + if ($LocalOnly) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] LocalOnly requested; skipping online catalog download" + } + elseif ($Force -or -not (Test-Path $tempCatalogPath)) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $oemDriverPackCatalog" - $null = Invoke-WebRequest -Uri $oemDriverPackCatalog -OutFile $tempCatalogPackagePath -ErrorAction Stop + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $OemDriverPackCatalog" + $null = Invoke-WebRequest -Uri $OemDriverPackCatalog -OutFile $tempCatalogPackagePath -ErrorAction Stop if (Test-Path $tempCatalogPackagePath) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Validating $tempCatalogPackagePath" @@ -81,12 +107,16 @@ function Get-OSDCloudCatalogHp { } # Load catalog content - if (Test-Path $tempCatalogPath) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $tempCatalogPath" + if ($LocalOnly) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + [xml]$XmlCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw + } + elseif (Test-Path $tempCatalogPath) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $tempCatalogPath" [xml]$XmlCatalogContent = Get-Content -Path $tempCatalogPath -Raw } else { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $localDriverPackCatalog" - [xml]$XmlCatalogContent = Get-Content -Path $localDriverPackCatalog -Raw + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + [xml]$XmlCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw } # Validate catalog content diff --git a/OSDCloud/private/core/Get-OSDCloudCatalogLenovo.ps1 b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogLenovo.ps1 similarity index 75% rename from OSDCloud/private/core/Get-OSDCloudCatalogLenovo.ps1 rename to OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogLenovo.ps1 index f3648f9..165a4c6 100644 --- a/OSDCloud/private/core/Get-OSDCloudCatalogLenovo.ps1 +++ b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogLenovo.ps1 @@ -1,4 +1,4 @@ -function Get-OSDCloudCatalogLenovo { +function Get-OSDCoreDriverPackCatalogLenovo { <# .SYNOPSIS Downloads and parses the Lenovo driver pack catalog for Windows 11. @@ -8,11 +8,34 @@ function Get-OSDCloudCatalogLenovo { parses the XML to create a catalog of available Windows 11 driver packs. Falls back to offline catalog if download fails. + .PARAMETER LocalDriverPackCatalog + Path to the local fallback Lenovo catalog XML file. + + .PARAMETER OemDriverPackCatalog + URL to the online Lenovo driver pack catalog XML file. + + .PARAMETER Force + Forces download and rebuild of the temporary online catalog even when a + cached temp catalog file already exists. + + .PARAMETER LocalOnly + Uses only local catalog values and skips online catalog download. + .EXAMPLE - Get-OSDCloudCatalogLenovo + Get-OSDCoreDriverPackCatalogLenovo Retrieves the Lenovo driver pack catalog for Windows 11. + .EXAMPLE + Get-OSDCoreDriverPackCatalogLenovo -Force + + Forces a fresh online download of the Lenovo catalog before parsing. + + .EXAMPLE + Get-OSDCoreDriverPackCatalogLenovo -LocalOnly + + Processes only local catalog values without any online download checks. + .OUTPUTS PSCustomObject[] Returns custom objects with driver pack information including Name, Model, @@ -22,24 +45,39 @@ function Get-OSDCloudCatalogLenovo { Catalog is downloaded from https://download.lenovo.com/cdrt/td/catalogv2.xml #> [CmdletBinding()] - param () + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$LocalDriverPackCatalog = (Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) 'core\driverpacks\lenovo.xml'), + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$OemDriverPackCatalog = 'https://download.lenovo.com/cdrt/td/catalogv2.xml', + + [Parameter(Mandatory = $false)] + [switch]$Force, + + [Parameter(Mandatory = $false)] + [switch]$LocalOnly + ) begin { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" #================================================= # Catalogs - $localDriverPackCatalog = Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) $OSDCloudModule.lenovo.driverpackcataloglocal - $oemDriverPackCatalog = $OSDCloudModule.lenovo.driverpackcatalogoem $tempCatalogPath = "$($env:TEMP)\osdcloud-driverpack-lenovo.xml" #================================================= # Build realtime catalog from online source, if fails fallback to offline catalog try { - if (-not (Test-Path $tempCatalogPath)) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $oemDriverPackCatalog" - $sourceContent = Invoke-RestMethod -Uri $oemDriverPackCatalog -UseBasicParsing -ErrorAction Stop + if ($LocalOnly) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] LocalOnly requested; skipping online catalog download" + } + elseif ($Force -or -not (Test-Path $tempCatalogPath)) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $OemDriverPackCatalog" + $sourceContent = Invoke-RestMethod -Uri $OemDriverPackCatalog -UseBasicParsing -ErrorAction Stop if ($sourceContent) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $tempCatalogPath" + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $tempCatalogPath" # Remove BOM (Byte Order Mark) from the beginning of the content $catalogContent = $sourceContent.Substring(3) $catalogContent | Out-File -FilePath $tempCatalogPath -Encoding utf8 -Force @@ -48,7 +86,7 @@ function Get-OSDCloudCatalogLenovo { } else { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Using temp catalog" if (Test-Path $tempCatalogPath) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $tempCatalogPath" + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $tempCatalogPath" [xml]$XmlCatalogContent = Get-Content -Path $tempCatalogPath -Raw } } @@ -58,9 +96,13 @@ function Get-OSDCloudCatalogLenovo { } # Load offline catalog if online catalog failed - if (-not $XmlCatalogContent) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $localDriverPackCatalog" - [xml]$XmlCatalogContent = Get-Content -Path $localDriverPackCatalog -Raw + if ($LocalOnly) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + [xml]$XmlCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw + } + elseif (-not $XmlCatalogContent) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + [xml]$XmlCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw } # Validate catalog content diff --git a/OSDCloud/private/core/Get-OSDCloudCatalogPanasonic.ps1 b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogPanasonic.ps1 similarity index 79% rename from OSDCloud/private/core/Get-OSDCloudCatalogPanasonic.ps1 rename to OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogPanasonic.ps1 index da42355..562e3f3 100644 --- a/OSDCloud/private/core/Get-OSDCloudCatalogPanasonic.ps1 +++ b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogPanasonic.ps1 @@ -1,30 +1,45 @@ -function Get-OSDCloudCatalogPanasonic { +function Get-OSDCoreDriverPackCatalogPanasonic { [CmdletBinding()] - param () + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$LocalDriverPackCatalog = (Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) 'core\driverpacks\panasonic.json'), + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$OemDriverPackCatalog = 'https://pna-b2b-storage-mkt.s3.amazonaws.com/computer/software/apps/Panasonic.json', + + [Parameter(Mandatory = $false)] + [switch]$Force, + + [Parameter(Mandatory = $false)] + [switch]$LocalOnly + ) begin { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" #================================================= # Catalogs - $localDriverPackCatalog = Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) $OSDCloudModule.panasonic.driverpackcataloglocal - $oemDriverPackCatalog = $OSDCloudModule.panasonic.driverpackcatalogoem $tempCatalogPath = "$($env:TEMP)\osdcloud-driverpack-panasonic.json" #================================================= # Build realtime catalog from online source, if fails fallback to offline catalog try { - if (-not (Test-Path $tempCatalogPath)) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $oemDriverPackCatalog" - $sourceContent = Invoke-RestMethod -Uri $oemDriverPackCatalog -UseBasicParsing -ErrorAction Stop + if ($LocalOnly) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] LocalOnly requested; skipping online catalog download" + } + elseif ($Force -or -not (Test-Path $tempCatalogPath)) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $OemDriverPackCatalog" + $sourceContent = Invoke-RestMethod -Uri $OemDriverPackCatalog -UseBasicParsing -ErrorAction Stop if ($sourceContent) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $tempCatalogPath" + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $tempCatalogPath" $sourceContent | Out-File -FilePath $tempCatalogPath -Encoding utf8 -Force $JsonCatalogContent = $sourceContent } } else { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Using temp Panasonic driver pack catalog" if (Test-Path $tempCatalogPath) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $tempCatalogPath" + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $tempCatalogPath" $JsonCatalogContent = Get-Content -Path $tempCatalogPath -Raw | ConvertFrom-Json } } @@ -34,9 +49,13 @@ function Get-OSDCloudCatalogPanasonic { } # Load offline catalog if online catalog failed - if (-not $JsonCatalogContent) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $localDriverPackCatalog" - $JsonCatalogContent = Get-Content -Path $localDriverPackCatalog -Raw | ConvertFrom-Json + if ($LocalOnly) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + $JsonCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw | ConvertFrom-Json + } + elseif (-not $JsonCatalogContent) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + $JsonCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw | ConvertFrom-Json } # Validate catalog content diff --git a/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogSurface.ps1 b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogSurface.ps1 new file mode 100644 index 0000000..b0ab383 --- /dev/null +++ b/OSDCloud/private/core-driverpack/Get-OSDCoreDriverPackCatalogSurface.ps1 @@ -0,0 +1,431 @@ +function Get-OSDCoreDriverPackCatalogSurface { + <# + .SYNOPSIS + Retrieves the Microsoft Surface driver pack catalog, enriching entries from live download pages. + + .DESCRIPTION + Loads the bundled surface.json catalog as the offline base. For entries that include an + UpdatePage URL, the function scrapes the corresponding Microsoft download page to find the + newest available MSI and updates FileName, Url, and ReleaseDate accordingly. + Results are cached in $env:TEMP so subsequent calls within the same session skip network + requests. Falls back to base JSON values when a page cannot be reached. + + .PARAMETER LocalDriverPackCatalog + Path to the local fallback Surface catalog JSON file. + + .PARAMETER Force + Forces bypass of the temp cache and rebuilds the enriched catalog from the + local Surface catalog. + + .PARAMETER LocalOnly + Uses only local catalog values and skips connectivity probing and all live + UpdatePage checks. + + .EXAMPLE + Get-OSDCoreDriverPackCatalogSurface + + Returns all Surface driver pack entries, with live URLs where available. + + .EXAMPLE + Get-OSDCoreDriverPackCatalogSurface -Verbose + + Returns all Surface driver pack entries with verbose progress output. + + .EXAMPLE + Get-OSDCoreDriverPackCatalogSurface -Force + + Bypasses the temp cache and rebuilds the enriched catalog. + + .EXAMPLE + Get-OSDCoreDriverPackCatalogSurface -LocalOnly + + Processes only local catalog values without any live network checks. + + .OUTPUTS + PSCustomObject[] + Objects with CatalogVersion, ReleaseDate, Name, Manufacturer, Model, SystemId, FileName, + Url, OperatingSystem, OSArchitecture, and HashMD5 properties. + + .NOTES + Base catalog: core/driverpacks/surface.json (bundled with the module) + Temp cache: $env:TEMP\osdcloud-driverpack-surface.json + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$LocalDriverPackCatalog = (Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) 'core\driverpacks\surface.json'), + + [Parameter(Mandatory = $false)] + [switch]$Force, + + [Parameter(Mandatory = $false)] + [switch]$LocalOnly + ) + + begin { + $Error.Clear() + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" + #================================================= + # Paths + $tempCatalogPath = "$env:TEMP\osdcloud-driverpack-surface.json" + #================================================= + # Load from temp cache if available + $useCache = $false + if ((-not $Force) -and (Test-Path $tempCatalogPath)) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $tempCatalogPath" + $JsonCatalogContent = Get-Content -Path $tempCatalogPath -Raw -Encoding UTF8 | ConvertFrom-Json + $useCache = $true + } + else { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Indexing $LocalDriverPackCatalog" + $JsonCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw -Encoding UTF8 | ConvertFrom-Json + } + + if (-not $JsonCatalogContent) { + $errorRecord = [System.Management.Automation.ErrorRecord]::new( + [System.Exception]::new("Failed to load Surface driver pack catalog"), + 'CatalogLoadFailed', + [System.Management.Automation.ErrorCategory]::InvalidData, + $LocalDriverPackCatalog + ) + $PSCmdlet.ThrowTerminatingError($errorRecord) + } + } + + process { + #================================================= + # Return cached results immediately + #================================================= + if ($useCache) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Returning $($JsonCatalogContent.Count) entries from cache" + $JsonCatalogContent + return + } + + #================================================= + # Build enriched catalog from base JSON + #================================================= + $processStopwatch = [System.Diagnostics.Stopwatch]::StartNew() + $CatalogVersion = Get-Date -Format yy.MM.dd + $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' + $msiPattern = 'https://download\.microsoft\.com/download/[^"''<>\s]+\.msi' + $datePattern = 'Date Published[^:]*:[^\d]*(\d{1,2}/\d{1,2}/\d{4})' + $parallelThrottle = 4 + $updatePageCache = @{} + $urlReachabilityCache = @{} + $networkCalls = 0 + $fallbackCalls = 0 + $cacheHits = 0 + $reachabilityChecks = 0 + $reachabilityMisses = 0 + $isOnline = $true + $skipLiveUpdates = [bool]$LocalOnly + + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Building Surface driver pack catalog (CatalogVersion: $CatalogVersion)" + + # If a device OSDProduct is known, limit live UpdatePage checks to the matching entry only + $osdProduct = if ($global:OSDCoreDevice -and $global:OSDCoreDevice.OSDProduct) { $global:OSDCoreDevice.OSDProduct } else { $null } + $escapedOsdProduct = if ($osdProduct) { [regex]::Escape($osdProduct) } else { $null } + if ($osdProduct) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDProduct filter active: only fetching live data for SystemId matching '$osdProduct'" + } + + # Determine online/offline state from the first catalog URL that does not require UpdatePage scraping. + $connectivityProbeUrl = ( + $JsonCatalogContent | + Where-Object { $_.Url -and (-not $_.UpdatePage) } | + Select-Object -First 1 + ).Url + + if ($LocalOnly) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] LocalOnly requested; skipping connectivity probe and live updates" + } + elseif ($connectivityProbeUrl) { + try { + $probe = Invoke-WebRequest -Uri $connectivityProbeUrl -Method Head -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -TimeoutSec 10 -ErrorAction Stop + $isOnline = ($probe.StatusCode -ge 200 -and $probe.StatusCode -lt 400) + } + catch { + try { + $probe = Invoke-WebRequest -Uri $connectivityProbeUrl -Method Get -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -TimeoutSec 10 -ErrorAction Stop + $isOnline = ($probe.StatusCode -ge 200 -and $probe.StatusCode -lt 400) + } + catch { + $isOnline = $false + } + } + } + + if (-not $isOnline) { + $skipLiveUpdates = $true + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Offline detected from connectivity probe; skipping all live updates and using local catalog values" + } + + # Probe existing target URLs first; only scrape UpdatePage when the current URL is unreachable. + $uniqueUpdatePages = @() + if (-not $skipLiveUpdates) { + $targetItems = @( + $JsonCatalogContent | Where-Object { + $_.UpdatePage -and ((-not $escapedOsdProduct) -or ($_.SystemId -match $escapedOsdProduct)) + } + ) + $itemsNeedingUpdate = @( + foreach ($item in $targetItems) { + $targetUrl = $item.Url + $isReachable = $false + + if (-not [string]::IsNullOrWhiteSpace($targetUrl)) { + if ($urlReachabilityCache.ContainsKey($targetUrl)) { + $isReachable = [bool]$urlReachabilityCache[$targetUrl] + } + else { + $reachabilityChecks++ + try { + $probe = Invoke-WebRequest -Uri $targetUrl -Method Head -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -TimeoutSec 10 -ErrorAction Stop + $isReachable = ($probe.StatusCode -ge 200 -and $probe.StatusCode -lt 400) + } + catch { + try { + $probe = Invoke-WebRequest -Uri $targetUrl -Method Get -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -TimeoutSec 10 -ErrorAction Stop + $isReachable = ($probe.StatusCode -ge 200 -and $probe.StatusCode -lt 400) + } + catch { + $isReachable = $false + } + } + + $urlReachabilityCache[$targetUrl] = $isReachable + } + } + + if (-not $isReachable) { + $reachabilityMisses++ + $item + } + } + ) + + $uniqueUpdatePages = @($itemsNeedingUpdate.UpdatePage | Sort-Object -Unique) + } + + if ($uniqueUpdatePages.Count -gt 0) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Resolving $($uniqueUpdatePages.Count) unique UpdatePage URLs" + + if ($PSVersionTable.PSVersion.Major -ge 7) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Using parallel UpdatePage resolution (ThrottleLimit: $parallelThrottle)" + $resolvedPages = $uniqueUpdatePages | ForEach-Object -Parallel { + $updatePage = $_ + $result = [ordered]@{ + UpdatePage = $updatePage + Error = $null + FileName = $null + Url = $null + ReleaseDate = $null + UsedFallback = $false + } + + try { + $response = Invoke-WebRequest -Uri $updatePage -UseBasicParsing -UserAgent $using:userAgent -MaximumRedirection 5 -ErrorAction Stop + $html = $response.Content + + $allMsi = @( + [regex]::Matches($html, $using:msiPattern) | + ForEach-Object { $_.Value } | + Select-Object -Unique + ) + + if ($allMsi.Count -eq 0 -and $updatePage -match '[?&]id=(\d+)') { + $confirmUri = "https://www.microsoft.com/en-us/download/confirmation.aspx?id=$($Matches[1])" + $result.UsedFallback = $true + $response = Invoke-WebRequest -Uri $confirmUri -UseBasicParsing -UserAgent $using:userAgent -MaximumRedirection 5 -ErrorAction Stop + $html = $response.Content + $allMsi = @( + [regex]::Matches($html, $using:msiPattern) | + ForEach-Object { $_.Value } | + Select-Object -Unique + ) + } + + if ($allMsi.Count -gt 0) { + $win11Uris = @($allMsi | Where-Object { $_ -match 'Win11' }) + $candidates = if ($win11Uris.Count -gt 0) { $win11Uris } else { $allMsi } + $bestUri = $candidates | + Sort-Object { + if ($_ -match '_(\d{5})_') { [int]$Matches[1] } else { 0 } + } -Descending | + Select-Object -First 1 + + $newDate = $null + $now = [datetime]::UtcNow + foreach ($m in [regex]::Matches($html, $using:datePattern)) { + try { + $parsed = [datetime]::ParseExact($m.Groups[1].Value, 'M/d/yyyy', $null) + if ($parsed.Year -ge 2015 -and $parsed -le $now.AddMonths(3)) { + $newDate = $parsed.ToString('yy.MM.dd') + break + } + } + catch { } + } + + $result.FileName = $bestUri -replace '.+/', '' + $result.Url = $bestUri + $result.ReleaseDate = $newDate + } + else { + $result.Error = 'No MSI links found' + } + } + catch { + $result.Error = $_.Exception.Message + } + + [PSCustomObject]$result + } -ThrottleLimit $parallelThrottle + + foreach ($resolved in $resolvedPages) { + if (-not $resolved) { continue } + $networkCalls++ + if ($resolved.UsedFallback) { $fallbackCalls++ } + $updatePageCache[$resolved.UpdatePage] = @{ + Error = $resolved.Error + FileName = $resolved.FileName + Url = $resolved.Url + ReleaseDate = $resolved.ReleaseDate + } + } + } + else { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] PowerShell < 7 detected, using sequential UpdatePage resolution" + foreach ($updatePage in $uniqueUpdatePages) { + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $updatePage" + $networkCalls++ + try { + $response = Invoke-WebRequest -Uri $updatePage -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -ErrorAction Stop + $html = $response.Content + + $allMsi = @( + [regex]::Matches($html, $msiPattern) | + ForEach-Object { $_.Value } | + Select-Object -Unique + ) + + if ($allMsi.Count -eq 0 -and $updatePage -match '[?&]id=(\d+)') { + $confirmUri = "https://www.microsoft.com/en-us/download/confirmation.aspx?id=$($Matches[1])" + Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $confirmUri" + $fallbackCalls++ + $networkCalls++ + $response = Invoke-WebRequest -Uri $confirmUri -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -ErrorAction Stop + $html = $response.Content + $allMsi = @( + [regex]::Matches($html, $msiPattern) | + ForEach-Object { $_.Value } | + Select-Object -Unique + ) + } + + if ($allMsi.Count -gt 0) { + $win11Uris = @($allMsi | Where-Object { $_ -match 'Win11' }) + $candidates = if ($win11Uris.Count -gt 0) { $win11Uris } else { $allMsi } + $bestUri = $candidates | + Sort-Object { + if ($_ -match '_(\d{5})_') { [int]$Matches[1] } else { 0 } + } -Descending | + Select-Object -First 1 + + $newDate = $null + $now = [datetime]::UtcNow + foreach ($m in [regex]::Matches($html, $datePattern)) { + try { + $parsed = [datetime]::ParseExact($m.Groups[1].Value, 'M/d/yyyy', $null) + if ($parsed.Year -ge 2015 -and $parsed -le $now.AddMonths(3)) { + $newDate = $parsed.ToString('yy.MM.dd') + break + } + } + catch { } + } + + $updatePageCache[$updatePage] = @{ + Error = $null + FileName = ($bestUri -replace '.+/', '') + Url = $bestUri + ReleaseDate = $newDate + } + } + else { + $updatePageCache[$updatePage] = @{ Error = 'No MSI links found' } + } + } + catch { + $updatePageCache[$updatePage] = @{ Error = $_.Exception.Message } + } + } + } + } + + $Results = foreach ($item in $JsonCatalogContent) { + $releaseDate = $item.ReleaseDate + $fileName = $item.FileName + $url = $item.Url + + $isTargetDevice = (-not $escapedOsdProduct) -or ($item.SystemId -match $escapedOsdProduct) + + if ($item.UpdatePage -and $isTargetDevice) { + $updatePage = $item.UpdatePage + + if ($updatePageCache.ContainsKey($updatePage)) { + $cacheHits++ + $cached = $updatePageCache[$updatePage] + if (-not $cached.Error) { + $fileName = $cached.FileName + $url = $cached.Url + if ($cached.ReleaseDate) { + $releaseDate = $cached.ReleaseDate + } + } + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Cache hit for $($item.Model) -> $fileName" + } + else { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] No pre-resolved cache entry for $($item.Model), using base values" + } + } + + # Rebuild the Name bracket date with current releaseDate + $baseName = $item.Name -replace '\s*\[.*?\]$', '' + $displayName = "$baseName [$releaseDate]" + + $objectProperties = [Ordered]@{ + CatalogVersion = $CatalogVersion + ReleaseDate = $releaseDate + Name = $displayName + Manufacturer = $item.Manufacturer + Model = $item.Model + SystemId = $item.SystemId + FileName = $fileName + Url = $url + OperatingSystem = $item.OperatingSystem + OSArchitecture = $item.OSArchitecture + HashMD5 = $item.HashMD5 + UpdatePage = $item.UpdatePage + } + [PSCustomObject]$objectProperties + } + + #================================================= + # Save enriched results to temp cache + #================================================= + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Saving $($Results.Count) entries to $tempCatalogPath" + $Results | ConvertTo-Json -Depth 10 | Out-File -FilePath $tempCatalogPath -Encoding utf8 -Force + + $processStopwatch.Stop() + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Performance: ReachabilityChecks=$reachabilityChecks, ReachabilityMisses=$reachabilityMisses, UpdatePages=$($uniqueUpdatePages.Count), CacheHits=$cacheHits, NetworkCalls=$networkCalls, FallbackCalls=$fallbackCalls, ElapsedMs=$($processStopwatch.ElapsedMilliseconds)" + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found $($Results.Count) Surface driver packs" + $Results + } + + end { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" + } +} diff --git a/OSDCloud/private/core/Get-CoreOperatingSystems.ps1 b/OSDCloud/private/core-operatingsystem/Get-ModuleCoreOperatingSystems.ps1 similarity index 64% rename from OSDCloud/private/core/Get-CoreOperatingSystems.ps1 rename to OSDCloud/private/core-operatingsystem/Get-ModuleCoreOperatingSystems.ps1 index 95dbf01..d6acf73 100644 --- a/OSDCloud/private/core/Get-CoreOperatingSystems.ps1 +++ b/OSDCloud/private/core-operatingsystem/Get-ModuleCoreOperatingSystems.ps1 @@ -1,36 +1,53 @@ -<#================================================================================= - Get-CoreOperatingSystems - ================================================================================ - - Returns XML content entries from core\operatingsystems as objects - ================================================================================ +function Get-ModuleCoreOperatingSystems { + <# .SYNOPSIS - Gets Microsoft catalog operating system entries from XML files. + Gets normalized module core operating system catalog records. .DESCRIPTION - Enumerates all XML files under the module path core\operatingsystems, - parses each File node, and returns the node properties as - PowerShell objects. + Reads operating system catalog XML files from core\operatingsystems under the module root, + converts each PublishedMedia file node into a PowerShell object, removes excluded metadata + properties, and normalizes duplicate properties. Duplicate catalog rows are grouped by + FilePath, FileName, LanguageCode, and Architecture, then the preferred row is selected + by hash availability. .EXAMPLE - PS C:\> Get-CoreOperatingSystems - Returns all operating system entries from the XML catalogs. + Get-ModuleCoreOperatingSystems + + Returns all normalized catalog records discovered in the module core operating systems cache. + + .EXAMPLE + Get-ModuleCoreOperatingSystems | Where-Object { $_.LanguageCode -eq 'en-us' } + + Returns only catalog records for en-us language media. + + .INPUTS + None + You cannot pipe input to this function. + + .OUTPUTS + PSCustomObject[] + Normalized raw catalog records imported from module XML metadata. + + .LINK + https://github.com/OSDeploy/OSD/tree/master/docs .NOTES - Author: OSDeploy - Version: 1.0 -=================================================================================#> -function Get-CoreOperatingSystems { + Author: David Segura - Recast Software + 2026-07-22 - Initial help block created + 2026-08-05 - Expanded help content and examples + #> [CmdletBinding()] [OutputType([pscustomobject[]])] param () $ErrorActionPreference = 'Stop' $records = @() + $mctRecords = @() $srcRoot = Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) 'core\operatingsystems' foreach ($file in (Get-ChildItem -Path $srcRoot -Filter '*.xml' -Recurse -File | Sort-Object FullName)) { - Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Importing $($file.FullName)" + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Importing $($file.FullName)" $xml = [xml](Get-Content -Path $file.FullName -Raw) $fileNodes = $xml.MCT.Catalogs.Catalog.PublishedMedia.Files.File @@ -83,11 +100,11 @@ function Get-CoreOperatingSystems { } } - $records += [pscustomobject]$properties + $mctRecords += [pscustomobject]$properties } } - $records = $records | + $mctRecords = $mctRecords | Group-Object -Property FilePath, FileName, LanguageCode, Architecture | ForEach-Object { $_.Group | @@ -96,5 +113,9 @@ function Get-CoreOperatingSystems { } | Sort-Object -Property FilePath, FileName, LanguageCode, Architecture - return $records + if (-not $mctRecords) { + return $records + } + + return $mctRecords } diff --git a/OSDCloud/private/core/Get-OSDCloudCoreOperatingSystems.ps1 b/OSDCloud/private/core-operatingsystem/Get-OSDCloudCoreOperatingSystems.ps1 similarity index 76% rename from OSDCloud/private/core/Get-OSDCloudCoreOperatingSystems.ps1 rename to OSDCloud/private/core-operatingsystem/Get-OSDCloudCoreOperatingSystems.ps1 index 16685a8..1524b05 100644 --- a/OSDCloud/private/core/Get-OSDCloudCoreOperatingSystems.ps1 +++ b/OSDCloud/private/core-operatingsystem/Get-OSDCloudCoreOperatingSystems.ps1 @@ -1,58 +1,61 @@ -<#================================================================================= - Get-OSDCloudCoreOperatingSystems - ================================================================================ - - Retrieves all operating system records from Microsoft catalogs - - Uses Get-CoreOperatingSystems records and extracts OS build, version, - architecture, language, and activation information - - Returns sorted array of operating system objects - ================================================================================ +function Get-OSDCloudCoreOperatingSystems { + <# .SYNOPSIS - Retrieves all operating system records from the OSDCloud catalog + Gets parsed operating system records for OSDCloud selection workflows. .DESCRIPTION - Imports operating system metadata from Get-CoreOperatingSystems, - then parses file names and catalog properties to extract OS build - numbers, versions, architecture, language codes, and download - information. - - .PARAMETER None - This function does not accept parameters. + Converts raw module catalog records from Get-ModuleCoreOperatingSystems into + normalized OSDCloud operating system objects. The function maps build numbers + to known Windows releases, derives build versions, normalizes architecture, + identifies activation channel, and emits sorted records with media metadata + such as language, size, file name, path, and hashes. .EXAMPLE - PS C:\> Get-OSDCloudCoreOperatingSystems - Returns all available operating system records + Get-OSDCloudCoreOperatingSystems + + Returns all available OSDCloud operating system records. .EXAMPLE - PS C:\> Get-OSDCloudCoreOperatingSystems | Where-Object { $_.OSName -eq 'Windows 11' } - Returns only Windows 11 operating systems + Get-OSDCloudCoreOperatingSystems | Where-Object { $_.OSName -eq 'Windows 11' } + + Returns only Windows 11 operating system records. .EXAMPLE - PS C:\> Get-OSDCloudCoreOperatingSystems | Group-Object OperatingSystem - Groups operating systems by major version + Get-OSDCloudCoreOperatingSystems | Group-Object OSArchitecture - .NOTES - Author: OSDeploy - Version: 1.0 - GitHub: https://github.com/OSDeploy + Groups results by normalized architecture. + + .INPUTS + None + You cannot pipe input to this function. + + .OUTPUTS + PSCustomObject[] + Parsed operating system records for OSDCloud catalog operations. .LINK - https://www.osdeploy.com/ -=================================================================================#> -function Get-OSDCloudCoreOperatingSystems { + https://www.osdeploy.com/ + + .NOTES + Author: OSDeploy + 2026-08-05 - Standardized and expanded comment-based help + #> [CmdletBinding()] [OutputType([pscustomobject[]])] param () + $ErrorActionPreference = 'Stop' $records = @() + $mctRecords = @() - $mctRecords = Get-CoreOperatingSystems + $mctRecords = Get-ModuleCoreOperatingSystems if (-not $mctRecords) { return $records } foreach ($node in ($mctRecords | Sort-Object FileName, LanguageCode, Architecture)) { - Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Processing $($node.FileName)" + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Processing $($node.FileName)" if ([string]::IsNullOrWhiteSpace($node.FileName) -or $node.FileName.Length -lt 5) { continue diff --git a/OSDCloud/private/core-operatingsystem/Get-OSDCoreOperatingSystems.ps1 b/OSDCloud/private/core-operatingsystem/Get-OSDCoreOperatingSystems.ps1 new file mode 100644 index 0000000..e81e8e8 --- /dev/null +++ b/OSDCloud/private/core-operatingsystem/Get-OSDCoreOperatingSystems.ps1 @@ -0,0 +1,190 @@ +function Get-OSDCoreOperatingSystems { + <# + .SYNOPSIS + Gets parsed OSD core operating system catalog entries. + + .DESCRIPTION + Imports normalized raw catalog records from Get-ModuleCoreOperatingSystems and + transforms them into OSD core operating system objects used by selection and + deployment workflows. The function derives build identity, Windows family and + release, normalized architecture, activation channel, and compatibility flags, + then returns unique sorted records. + + .EXAMPLE + Get-OSDCoreOperatingSystems + + Returns all available parsed core operating system records. + + .EXAMPLE + Get-OSDCoreOperatingSystems | Where-Object Version -eq 'Windows 11' + + Returns only Windows 11 operating system records. + + .EXAMPLE + Get-OSDCoreOperatingSystems | Where-Object { $_.Architecture -eq 'arm64' } + + Returns only arm64 operating system records. + + .INPUTS + None + You cannot pipe input to this function. + + .OUTPUTS + PSCustomObject[] + Parsed operating system records with OSD-specific properties. + + .LINK + https://github.com/OSDeploy/OSD/tree/master/docs + + .NOTES + Author: David Segura - Recast Software + 2026-07-22 - Initial help block created + 2026-08-05 - Expanded help content and examples + #> + [CmdletBinding()] + [OutputType([pscustomobject[]])] + param () + + $ErrorActionPreference = 'Stop' + $records = @() + $mctRecords = @() + + $mctRecords = Get-ModuleCoreOperatingSystems + + if (-not $mctRecords) { + return $records + } + + foreach ($node in ($mctRecords | Sort-Object FileName, LanguageCode, Architecture)) { + Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Processing $($node.FileName)" + + if ([string]::IsNullOrWhiteSpace($node.FileName) -or $node.FileName.Length -lt 5) { + continue + } + #================================================= + # OSBuild + # Get the OSBuild from the FileName + $OSBuild = $node.FileName.Substring(0, 5) + #================================================= + # OperatingSystem / OSName / OSVersion + # 19045 = Windows 10 22H2 + # 22000 = Windows 11 21H2 + # 22621 = Windows 11 22H2 + # 22631 = Windows 11 23H2 + # 26100 = Windows 11 24H2 + # 26200 = Windows 11 25H2 + # 28000 = Windows 11 26H1 + switch ($OSBuild) { + '19045' { $OSName = 'Windows 10'; $OSVersion = '22H2' } + '22000' { $OSName = 'Windows 11'; $OSVersion = '21H2' } + '22621' { $OSName = 'Windows 11'; $OSVersion = '22H2' } + '22631' { $OSName = 'Windows 11'; $OSVersion = '23H2' } + '26100' { $OSName = 'Windows 11'; $OSVersion = '24H2' } + '26200' { $OSName = 'Windows 11'; $OSVersion = '25H2' } + '28000' { $OSName = 'Windows 11'; $OSVersion = '26H1' } + default { continue } + } + #================================================= + # OSBuildVersion + # Combination of . + # Extract from FileName + #================================================= + $fileNameParts = $node.FileName -split '\.' + if ($fileNameParts.Count -lt 2) { + continue + } + $OSBuildVersion = "$($fileNameParts[0]).$($fileNameParts[1])" + #================================================= + # OSArchitecture + # Avoids confusion between x64 releases (amd64/arm64) + #================================================= + if ($node.Architecture -match 'x64') { + $OSArchitecture = 'amd64' + } elseif ($node.Architecture -match 'arm64') { + $OSArchitecture = 'arm64' + } else { + $OSArchitecture = 'x86' + continue + } + #================================================= + # OSActivation + #================================================= + if ($node.FileName -match 'clientconsumer_ret') { + $OSActivation = 'Retail' + } + elseif ($node.FileName -match 'CLIENTBUSINESS_VOL') { + $OSActivation = 'Volume' + } + else { + $OSActivation = 'Unknown' + continue + } + #================================================= + # Win10 / Win11 + #================================================= + if ($OSName -eq 'Windows 10') { + $Win10 = $true + $Win11 = $false + } + elseif ($OSName -eq 'Windows 11') { + $Win10 = $false + $Win11 = $true + } + else { + $Win10 = $false + $Win11 = $false + } + #================================================= + # OSD Module Properties + #================================================= + # DisplayName should be in the format "Win11-25H2-amd64" + $DisplayName = "$OSName $OSVersion $OSArchitecture $($node.LanguageCode) $OSActivation $OSBuildVersion" + #================================================= + # ObjectProperties + #================================================= + <# + Status : + ReleaseDate : 2023-12-04 + Name : Windows 10 22H2 x64 ar-sa Retail 19045.3803 + Version : Windows 10 + ReleaseID : 22H2 + Architecture : x64 + Language : ar-sa + Activation : Retail + Build : 19045.3803 + FileName : 19045.3803.231204-0204.22h2_release_svc_refresh_CLIENTCONSUMER_RET_x64FRE_ar-sa.esd + ImageIndex : + ImageName : + Url : http://dl.delivery.mp.microsoft.com/filestreamingservice/files/39d366c6-bb66-4938-9a78-0670eda8304d/19045.3803.231204-0204.22h2_release_svc_refresh_CLIENTCONSUMER_RET_x64FRE_ar-sa.esd + SHA1 : 2119ef0efd432f98cdccdf525cd17fcceacef111 + UpdateID : + Win10 : True + Win11 : False + #> + + $records += [pscustomobject]@{ + Status = $null + ReleaseDate = $null + Name = $DisplayName + Version = $OSName + ReleaseID = $OSVersion + Architecture = $OSArchitecture + Language = $node.LanguageCode + Activation = $OSActivation + Build = $OSBuildVersion + FileName = $node.FileName + ImageIndex = $node.ImageIndex + ImageName = $node.ImageName + Url = $node.FilePath + SHA1 = $node.Sha1 + SHA256 = $node.Sha256 + UpdateID = $node.UpdateID + Win10 = $Win10 + Win11 = $Win11 + } + } + + $records = $records | Sort-Object -Property Url -Unique + $records = $records | Sort-Object -Property Name + return $records +} diff --git a/OSDCloud/private/core-operatingsystem/Set-OSDCoreOperatingSystemCloudObject.ps1 b/OSDCloud/private/core-operatingsystem/Set-OSDCoreOperatingSystemCloudObject.ps1 new file mode 100644 index 0000000..100f6c4 --- /dev/null +++ b/OSDCloud/private/core-operatingsystem/Set-OSDCoreOperatingSystemCloudObject.ps1 @@ -0,0 +1,121 @@ +function Set-OSDCoreOperatingSystemCloudObject { + <# + .SYNOPSIS + Selects and sets the global OSD core operating system object. + + .DESCRIPTION + Filters the preloaded operating system catalog using activation, architecture, + language, release ID, and version criteria, then sets + $global:OSDCoreOperatingSystemCloudObject to the best match. If multiple matches + are found, the highest build is selected. + + .PARAMETER OSActivation + Operating system activation channel used for catalog selection. + + .PARAMETER OSArchitecture + Operating system architecture used for catalog selection. + + .PARAMETER OSLanguageCode + Operating system language code used for catalog selection. + + .PARAMETER OSReleaseID + Operating system release identifier used for catalog selection. + + .PARAMETER OSVersion + Operating system family/version label used for catalog selection. + + .PARAMETER RefreshCatalog + Reloads $global:OSDCoreOperatingSystems from Get-OSDCoreOperatingSystems + before filtering. + + .EXAMPLE + Set-OSDCoreOperatingSystemCloudObject -OSArchitecture amd64 -OSReleaseID 25H2 -OSLanguageCode en-us + Selects the latest Windows 11 Retail amd64 en-us 25H2 catalog entry and sets + $global:OSDCoreOperatingSystemCloudObject. + + .EXAMPLE + Set-OSDCoreOperatingSystemCloudObject -OSActivation Volume -OSArchitecture arm64 -OSLanguageCode en-us -OSReleaseID 24H2 -RefreshCatalog + + Refreshes the catalog cache and selects the latest matching Volume arm64 record. + + .INPUTS + None + You cannot pipe input to this function. + + .OUTPUTS + PSCustomObject + The selected operating system object assigned to + $global:OSDCoreOperatingSystemCloudObject. + + .LINK + https://github.com/OSDeploy/OSD/tree/master/docs + + .NOTES + Author: David Segura - Recast Software + 2026-07-15 - Initial implementation to centralize OS catalog object selection. + 2026-08-05 - Expanded help content and examples + #> + [CmdletBinding()] + [OutputType([pscustomobject])] + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [ValidateSet('Retail','Volume')] + [string]$OSActivation = 'Retail', + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [ValidateSet('amd64','arm64')] + [string]$OSArchitecture = $env:PROCESSOR_ARCHITECTURE, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$OSLanguageCode = 'en-us', + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$OSReleaseID = '25H2', + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$OSVersion = 'Windows 11', + + [Parameter(Mandatory = $false)] + [switch]$RefreshCatalog + ) + + $normalizedArchitecture = $OSArchitecture.ToLowerInvariant() + $normalizedLanguageCode = $OSLanguageCode.ToLowerInvariant() + + if ($RefreshCatalog -or -not $global:OSDCoreOperatingSystems) { + $global:OSDCoreOperatingSystems = Get-OSDCoreOperatingSystems | Where-Object { $_.Architecture -match $OSArchitecture } + } + + if (-not $global:OSDCoreOperatingSystems) { + throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to load Operating Systems" + } + + $matches = $global:OSDCoreOperatingSystems | + Where-Object { $_.Activation -eq $OSActivation } | + Where-Object { $_.Architecture -match $normalizedArchitecture } | + Where-Object { $_.Language.ToLowerInvariant() -eq $normalizedLanguageCode } | + Where-Object { $_.ReleaseID -eq $OSReleaseID } | + Where-Object { $_.Version -eq $OSVersion } + + $global:OSDCoreOperatingSystemCloudObject = $matches | + Sort-Object -Property @{ Expression = { + try { + [version]($_.Build -replace '[^0-9\.]', '') + } + catch { + [version]'0.0' + } + }; Descending = $true } | + Select-Object -First 1 + + if (-not $global:OSDCoreOperatingSystemCloudObject) { + throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to find a matching operating system object for OSReleaseID '$OSReleaseID', OSArchitecture '$normalizedArchitecture', Activation '$OSActivation', Language '$normalizedLanguageCode', and OSVersion '$OSVersion'." + } + + return $global:OSDCoreOperatingSystemCloudObject +} diff --git a/OSDCloud/private/core/Get-OSDCloudDefaultOS.ps1 b/OSDCloud/private/core-operatingsystem/dev/Get-OSDCloudDefaultOS.ps1 similarity index 69% rename from OSDCloud/private/core/Get-OSDCloudDefaultOS.ps1 rename to OSDCloud/private/core-operatingsystem/dev/Get-OSDCloudDefaultOS.ps1 index 399209d..68e5641 100644 --- a/OSDCloud/private/core/Get-OSDCloudDefaultOS.ps1 +++ b/OSDCloud/private/core-operatingsystem/dev/Get-OSDCloudDefaultOS.ps1 @@ -1,45 +1,53 @@ -<#================================================================================= - Get-OSDCloudDefaultOS - ================================================================================ - - Gets a single operating system record based on system architecture and - language preferences - - Filters from available OS records using a priority order for language codes - ================================================================================ +function Get-OSDCloudDefaultOS { + <# .SYNOPSIS - Gets a single operating system record from the OSDCloud catalog + Gets the default OSDCloud operating system record for the current context. .DESCRIPTION - Retrieves operating system metadata from Microsoft catalogs and filters - based on processor architecture and language preferences. The function - evaluates language codes in a specific priority order and returns the - first matching operating system record. + Retrieves operating system records from Get-OSDCloudCoreOperatingSystems and + applies architecture and language preference filters to select a single default + record. Language preference order is: + 1) $global:OSDCLOUD_OSLANGUAGECODE + 2) $env:OSDCLOUD_OSLANGUAGECODE + 3) Get-Culture Name - .PARAMETER None - This function does not accept parameters. Filtering is done through - environment variables and global variables. + Architecture selection uses $env:PROCESSOR_ARCHITECTURE when available. + The first matching record is returned after filters are applied. .EXAMPLE - PS C:\> Get-OSDCloudDefaultOS - Returns the first matching operating system based on current culture + Get-OSDCloudDefaultOS + + Returns the first matching operating system based on the current architecture + and language preference inputs. .EXAMPLE - PS C:\> Get-OSDCloudDefaultOS | Format-List * - Displays all properties of the selected operating system + $env:OSDCLOUD_OSLANGUAGECODE = 'en-us' + Get-OSDCloudDefaultOS + + Returns an en-us operating system record when one exists for the selected + architecture. .EXAMPLE - PS C:\> $os = Get-OSDCloudDefaultOS - PS C:\> $os.FileName - Returns the download filename for the selected operating system + $record = Get-OSDCloudDefaultOS + $record.FileName - .NOTES - Author: OSDeploy - Version: 1.0 - GitHub: https://github.com/OSDeploy + Returns the media filename for the selected default record. + + .INPUTS + None + You cannot pipe input to this function. + + .OUTPUTS + PSCustomObject + A single operating system catalog record. .LINK - https://www.osdeploy.com/ -=================================================================================#> -function Get-OSDCloudDefaultOS { + https://www.osdeploy.com/ + + .NOTES + Author: OSDeploy + 2026-08-05 - Standardized and expanded comment-based help + #> [CmdletBinding()] [OutputType([pscustomobject])] param () diff --git a/OSDCloud/private/core/Get-OSDCloudCatalogSurface.ps1 b/OSDCloud/private/core/Get-OSDCloudCatalogSurface.ps1 deleted file mode 100644 index 6fb6044..0000000 --- a/OSDCloud/private/core/Get-OSDCloudCatalogSurface.ps1 +++ /dev/null @@ -1,220 +0,0 @@ -function Get-OSDCloudCatalogSurface { - <# - .SYNOPSIS - Retrieves the Microsoft Surface driver pack catalog, enriching entries from live download pages. - - .DESCRIPTION - Loads the bundled surface.json catalog as the offline base. For entries that include an - UpdatePage URL, the function scrapes the corresponding Microsoft download page to find the - newest available MSI and updates FileName, Url, and ReleaseDate accordingly. - Results are cached in $env:TEMP so subsequent calls within the same session skip network - requests. Falls back to base JSON values when a page cannot be reached. - - .EXAMPLE - Get-OSDCloudCatalogSurface - - Returns all Surface driver pack entries, with live URLs where available. - - .EXAMPLE - Get-OSDCloudCatalogSurface -Verbose - - Returns all Surface driver pack entries with verbose progress output. - - .OUTPUTS - PSCustomObject[] - Objects with CatalogVersion, ReleaseDate, Name, Manufacturer, Model, SystemId, FileName, - Url, OperatingSystem, OSArchitecture, and HashMD5 properties. - - .NOTES - Base catalog: core/driverpacks/surface.json (bundled with the module) - Temp cache: $env:TEMP\osdcloud-driverpack-surface.json - #> - [CmdletBinding()] - param () - - begin { - $Error.Clear() - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" - #================================================= - # Paths - $localCatalogPath = Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) $OSDCloudModule.surface.driverpackcataloglocal - $tempCatalogPath = "$env:TEMP\osdcloud-driverpack-surface.json" - #================================================= - # Load from temp cache if available - $useCache = $false - if (Test-Path $tempCatalogPath) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $tempCatalogPath" - $JsonCatalogContent = Get-Content -Path $tempCatalogPath -Raw -Encoding UTF8 | ConvertFrom-Json - $useCache = $true - } - else { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Loading $localCatalogPath" - $JsonCatalogContent = Get-Content -Path $localCatalogPath -Raw -Encoding UTF8 | ConvertFrom-Json - } - - if (-not $JsonCatalogContent) { - $errorRecord = [System.Management.Automation.ErrorRecord]::new( - [System.Exception]::new("Failed to load Surface driver pack catalog"), - 'CatalogLoadFailed', - [System.Management.Automation.ErrorCategory]::InvalidData, - $localCatalogPath - ) - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - } - - process { - #================================================= - # Return cached results immediately - #================================================= - if ($useCache) { - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Returning $($JsonCatalogContent.Count) entries from cache" - $JsonCatalogContent - return - } - - #================================================= - # Build enriched catalog from base JSON - #================================================= - $CatalogVersion = Get-Date -Format yy.MM.dd - $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' - $msiPattern = 'https://download\.microsoft\.com/download/[^"''<>\s]+\.msi' - $updatePageCache = @{} - - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Building Surface driver pack catalog (CatalogVersion: $CatalogVersion)" - - # If a device OSDProduct is known, limit live UpdatePage checks to the matching entry only - $osdProduct = if ($global:OSDCloudDevice -and $global:OSDCloudDevice.OSDProduct) { $global:OSDCloudDevice.OSDProduct } else { $null } - if ($osdProduct) { - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDProduct filter active: only fetching live data for SystemId matching '$osdProduct'" - } - - $Results = foreach ($item in $JsonCatalogContent) { - $releaseDate = $item.ReleaseDate - $fileName = $item.FileName - $url = $item.Url - - $isTargetDevice = (-not $osdProduct) -or ($item.SystemId -match [regex]::Escape($osdProduct)) - - if ($item.UpdatePage -and $isTargetDevice) { - $updatePage = $item.UpdatePage - - if ($updatePageCache.ContainsKey($updatePage)) { - $cached = $updatePageCache[$updatePage] - if (-not $cached.Error) { - $fileName = $cached.FileName - $url = $cached.Url - $releaseDate = $cached.ReleaseDate - } - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Cache hit for $($item.Model) -> $fileName" - } - else { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $updatePage" - try { - $response = Invoke-WebRequest -Uri $updatePage -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -ErrorAction Stop - $html = $response.Content - - $allMsi = @( - [regex]::Matches($html, $msiPattern) | - ForEach-Object { $_.Value } | - Select-Object -Unique - ) - - # Fallback: try the confirmation page if no MSI links on the details page - if ($allMsi.Count -eq 0) { - $pageId = [System.Web.HttpUtility]::ParseQueryString(([uri]$updatePage).Query)['id'] - $confirmUri = "https://www.microsoft.com/en-us/download/confirmation.aspx?id=$pageId" - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Downloading $confirmUri" - $response = Invoke-WebRequest -Uri $confirmUri -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -ErrorAction Stop - $html = $response.Content - $allMsi = @( - [regex]::Matches($html, $msiPattern) | - ForEach-Object { $_.Value } | - Select-Object -Unique - ) - } - - if ($allMsi.Count -gt 0) { - # Prefer Win11 MSIs; among those pick the highest OS build number in the filename - $win11Uris = @($allMsi | Where-Object { $_ -match 'Win11' }) - $candidates = if ($win11Uris.Count -gt 0) { $win11Uris } else { $allMsi } - $bestUri = $candidates | - Sort-Object { - if ($_ -match '_(\d{5})_') { [int]$Matches[1] } else { 0 } - } -Descending | - Select-Object -First 1 - - $newFileName = $bestUri -replace '.+/', '' - - # Extract Date Published from HTML - $newDate = $null - $now = [datetime]::UtcNow - foreach ($m in [regex]::Matches($html, 'Date Published[^:]*:[^\d]*(\d{1,2}/\d{1,2}/\d{4})')) { - try { - $parsed = [datetime]::ParseExact($m.Groups[1].Value, 'M/d/yyyy', $null) - if ($parsed.Year -ge 2015 -and $parsed -le $now.AddMonths(3)) { - $newDate = $parsed.ToString('yy.MM.dd') - break - } - } - catch { } - } - - $fileName = $newFileName - $url = $bestUri - $releaseDate = if ($newDate) { $newDate } else { $releaseDate } - - $updatePageCache[$updatePage] = @{ - Error = $null - FileName = $fileName - Url = $url - ReleaseDate = $releaseDate - } - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Updated $($item.Model) -> $fileName [$releaseDate]" - } - else { - $updatePageCache[$updatePage] = @{ Error = 'No MSI links found' } - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] No MSI links found for $($item.Model), using base values" - } - } - catch { - $updatePageCache[$updatePage] = @{ Error = $_.Exception.Message } - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Failed to check UpdatePage for $($item.Model): $($_.Exception.Message)" - } - } - } - - # Rebuild the Name bracket date with current releaseDate - $baseName = $item.Name -replace '\s*\[.*?\]$', '' - $displayName = "$baseName [$releaseDate]" - - $objectProperties = [Ordered]@{ - CatalogVersion = $CatalogVersion - ReleaseDate = $releaseDate - Name = $displayName - Manufacturer = $item.Manufacturer - Model = $item.Model - SystemId = $item.SystemId - FileName = $fileName - Url = $url - OperatingSystem = $item.OperatingSystem - OSArchitecture = $item.OSArchitecture - HashMD5 = $item.HashMD5 - } - New-Object -TypeName PSObject -Property $objectProperties - } - - #================================================= - # Save enriched results to temp cache - #================================================= - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Saving $($Results.Count) entries to $tempCatalogPath" - $Results | ConvertTo-Json -Depth 10 | Out-File -FilePath $tempCatalogPath -Encoding utf8 -Force - - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found $($Results.Count) Surface driver packs" - $Results - } - - end { - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" - } -} diff --git a/OSDCloud/private/core/Get-OSDCloudCoreDriverPacks.ps1 b/OSDCloud/private/core/Get-OSDCloudCoreDriverPacks.ps1 deleted file mode 100644 index 27f8529..0000000 --- a/OSDCloud/private/core/Get-OSDCloudCoreDriverPacks.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -<# -.SYNOPSIS -Retrieves driver pack information for the specified manufacturer and operating system architecture. - -.DESCRIPTION -Gets driver pack catalogs based on the device manufacturer and OS architecture. For AMD64 architecture, -manufacturer-specific catalogs are loaded. For ARM64 and other architectures, the default catalog is returned. -Supports Dell, HP, Lenovo, Microsoft (Surface), and generic devices. - -.PARAMETER Manufacturer -The device manufacturer name. Defaults to the value from $global:OSDCloudDevice.OSDManufacturer. -Supported values: Dell, HP, Lenovo, Microsoft, or any other value will use the Default catalog. - -.PARAMETER ProcessorArchitecture -The operating system architecture. Defaults to the value from $global:OSDCloudDevice.ProcessorArchitecture. -Typically 'amd64' or 'arm64'. - -.OUTPUTS -PSCustomObject -Array of driver pack objects containing driver information for the specified manufacturer and architecture. - -.EXAMPLE -PS> Get-OSDCloudCoreDriverPacks -Returns driver packs for the current device's manufacturer and architecture. - -.EXAMPLE -PS> Get-OSDCloudCoreDriverPacks -Manufacturer 'Dell' -ProcessorArchitecture 'amd64' -Returns driver packs for Dell devices with AMD64 architecture. - -.NOTES -Requires manufacturer-specific cmdlets (Get-OSDCloudCatalogDell, Get-OSDCloudCatalogHp, etc.) to be available. -#> -function Get-OSDCloudCoreDriverPacks { - [CmdletBinding()] - param ( - [System.String] - $Manufacturer = $global:OSDCloudDevice.OSDManufacturer, - [System.String] - $ProcessorArchitecture = $global:OSDCloudDevice.ProcessorArchitecture - ) - - # Load default catalog once - $GenericCatalogPath = Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) $OSDCloudModule.generic.driverpackcataloglocal - $DefaultCatalog = Get-Content -Path $GenericCatalogPath -Raw | ConvertFrom-Json - - if ($ProcessorArchitecture -eq 'amd64') { - $DriverPackValues = switch ($Manufacturer) { - 'Dell' { Get-OSDCloudCatalogDell } - 'HP' { Get-OSDCloudCatalogHp } - 'Lenovo' { Get-OSDCloudCatalogLenovo } - 'Microsoft' { Get-OSDCloudCatalogSurface } - default { $DefaultCatalog } - } - } - else { - if ($Manufacturer -eq 'Microsoft') { - $DriverPackValues = Get-OSDCloudCatalogSurface - } - else { - $DriverPackValues = $DefaultCatalog - } - } - - $DriverPackValues | Where-Object { $_.OSArchitecture -eq $ProcessorArchitecture } -} diff --git a/OSDCloud/private/datetime/Sync-InternetDateTime.ps1 b/OSDCloud/private/datetime/Sync-InternetDateTime.ps1 deleted file mode 100644 index 67b2646..0000000 --- a/OSDCloud/private/datetime/Sync-InternetDateTime.ps1 +++ /dev/null @@ -1,157 +0,0 @@ -function Sync-WinpeInternetDateTime { - <# - .SYNOPSIS - Synchronizes the system clock with internet time from Google. - - .DESCRIPTION - Retrieves the current time from Google's HTTP Date header and compares it with the local system time. - If the time difference exceeds the specified threshold and the system is running in WinPE (X: drive), - the function will update the system clock to match the internet time. - - Note: Uses HTTP instead of HTTPS to avoid certificate validation issues that may occur when the - system clock is significantly out of sync. - - .PARAMETER ThresholdMinutes - The minimum time difference in minutes required to trigger a synchronization warning or action. - Default is 5 minutes. - - .PARAMETER Force - When specified, actually updates the system clock when the time difference exceeds the threshold. - Without this parameter, the function only reports time differences without making changes. - - .PARAMETER PassThru - Returns an object containing the synchronization results including local time, internet time, - time difference, and whether the clock was updated. - - .EXAMPLE - Sync-WinpeInternetDateTime - - Checks the system time against internet time and reports any differences (in WinPE environment). - - .EXAMPLE - Sync-WinpeInternetDateTime -Force - - Checks and actually updates the system clock if the time difference exceeds the threshold. - - .EXAMPLE - Sync-WinpeInternetDateTime -ThresholdMinutes 30 -Force -PassThru - - Uses a 30-minute threshold, updates the clock if needed, and returns detailed synchronization results. - - .NOTES - This function only modifies the system clock when running in Windows Preinstallation Environment (WinPE), - detected by checking if the system drive is X:. - #> - [CmdletBinding(SupportsShouldProcess)] - param( - [Parameter()] - [ValidateRange(1, 1440)] - [int]$ThresholdMinutes = 5, - - [Parameter()] - [switch]$Force, - - [Parameter()] - [switch]$PassThru - ) - - begin { - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Starting internet time synchronization check" - } - - process { - $result = [PSCustomObject]@{ - LocalDateTime = $null - InternetDateTime = $null - DifferenceMinutes = $null - IsWinPE = $env:SystemDrive -eq "X:" - ClockUpdated = $false - Success = $false - ErrorMessage = $null - } - - try { - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Retrieving time from Google" - $googleResponse = Invoke-WebRequest -Uri "http://www.google.com" -UseBasicParsing -Method Head -ErrorAction Stop - $googleDateHeaderRaw = $googleResponse.Headers["Date"] - if ($googleDateHeaderRaw -is [System.Array]) { - $googleDateHeader = $googleDateHeaderRaw | Select-Object -First 1 - } - else { - $googleDateHeader = $googleDateHeaderRaw - } - - if ([string]::IsNullOrWhiteSpace($googleDateHeader)) { - $result.ErrorMessage = "No Date header received from Google" - Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] $($result.ErrorMessage)" - if ($PassThru) { return $result } - return - } - - $internetDateTime = [System.DateTimeOffset]::MinValue - $dateParseStyles = [System.Globalization.DateTimeStyles]::AllowWhiteSpaces -bor [System.Globalization.DateTimeStyles]::AssumeUniversal -bor [System.Globalization.DateTimeStyles]::AdjustToUniversal - $parsedDate = [System.DateTimeOffset]::TryParse($googleDateHeader, [System.Globalization.CultureInfo]::InvariantCulture, $dateParseStyles, [ref]$internetDateTime) - - if ($parsedDate) { - $result.LocalDateTime = Get-Date - $result.InternetDateTime = $internetDateTime.LocalDateTime - $result.Success = $true - - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Local time: $($result.LocalDateTime)" - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Internet time: $($result.InternetDateTime)" - } - else { - $result.ErrorMessage = "Unable to parse Date header from Google response: $googleDateHeader" - Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] $($result.ErrorMessage)" - if ($PassThru) { return $result } - return - } - } - catch { - $result.ErrorMessage = $_.Exception.Message - Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Failed to retrieve current time from Google. Error: $($result.ErrorMessage)" - if ($PassThru) { return $result } - return - } - - if ($result.LocalDateTime -and $result.InternetDateTime) { - $result.DifferenceMinutes = [math]::Round([math]::Abs(($result.InternetDateTime - $result.LocalDateTime).TotalMinutes)) - - if ($result.DifferenceMinutes -gt $ThresholdMinutes) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Time difference of $($result.DifferenceMinutes) minutes exceeds threshold of $ThresholdMinutes minutes" - - if ($result.IsWinPE) { - if ($Force) { - if ($PSCmdlet.ShouldProcess("System Clock", "Set to $($result.InternetDateTime)")) { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Setting system clock to internet time" - try { - $null = Set-Date -Date $result.InternetDateTime -ErrorAction Stop - $result.ClockUpdated = $true - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] System clock successfully updated" - } - catch { - $result.ErrorMessage = "Failed to set system clock: $($_.Exception.Message)" - Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] $($result.ErrorMessage)" - } - } - } - else { - Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] System clock is $($result.DifferenceMinutes) minutes out of sync with internet time" - Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Use -Force parameter to update the system clock" - } - } - else { - Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] System clock is $($result.DifferenceMinutes) minutes out of sync with internet time" - Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Please synchronize your system clock manually (not in WinPE environment)" - } - } - else { - Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] System clock is synchronized within threshold ($($result.DifferenceMinutes) minutes difference)" - } - } - - if ($PassThru) { - return $result - } - } -} diff --git a/OSDCloud/private/disk/disk-osdcloud-v1.ps1 b/OSDCloud/private/disk/disk-osdcloud-v1.ps1 index 829f346..5c41439 100644 --- a/OSDCloud/private/disk/disk-osdcloud-v1.ps1 +++ b/OSDCloud/private/disk/disk-osdcloud-v1.ps1 @@ -76,7 +76,7 @@ function New-OSDPartitionSystem { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Starting system partition workflow for disk $DiskNumber" if (-NOT ($PartitionStyle)) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] PartitionStyle was not supplied; inferring from current boot mode" - if ($global:OSDCloudDevice.IsUEFI -eq $true) { + if ($global:OSDCoreDevice.IsUEFI -eq $true) { $PartitionStyle = 'GPT' } else { $PartitionStyle = 'MBR' diff --git a/OSDCloud/private/disk/disk-osdcloud-v2.ps1 b/OSDCloud/private/disk/disk-osdcloud-v2.ps1 index 490a82c..841ec1e 100644 --- a/OSDCloud/private/disk/disk-osdcloud-v2.ps1 +++ b/OSDCloud/private/disk/disk-osdcloud-v2.ps1 @@ -76,7 +76,7 @@ function New-OSDCloudPartitionSystem { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Starting system partition workflow for disk $DiskNumber" if (-NOT ($PartitionStyle)) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] PartitionStyle was not supplied; inferring from current boot mode" - if ($global:OSDCloudDevice.IsUEFI -eq $true) { + if ($global:OSDCoreDevice.IsUEFI -eq $true) { $PartitionStyle = 'GPT' } else { $PartitionStyle = 'MBR' @@ -217,7 +217,7 @@ function New-OSDCloudPartitionWindows { #================================================= if (-NOT ($PartitionStyle)) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] PartitionStyle was not supplied; inferring from current boot mode" - if ($global:OSDCloudDevice.IsUEFI -eq $true) { + if ($global:OSDCoreDevice.IsUEFI -eq $true) { $PartitionStyle = 'GPT' } else { $PartitionStyle = 'MBR' @@ -429,7 +429,7 @@ function Clear-DeviceLocalDisk { # -PartitionStyle #================================================= if (-NOT ($PSBoundParameters.ContainsKey('PartitionStyle'))) { - if ($global:OSDCloudDevice.IsUEFI -eq $true) { + if ($global:OSDCoreDevice.IsUEFI -eq $true) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] IsUEFI = $true" $PartitionStyle = 'GPT' } else { @@ -1372,7 +1372,7 @@ function New-OSDCloudDisk { # -PartitionStyle #================================================= if (-NOT ($PSBoundParameters.ContainsKey('PartitionStyle'))) { - if ($global:OSDCloudDevice.IsUEFI -eq $true) { + if ($global:OSDCoreDevice.IsUEFI -eq $true) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] IsUEFI = $true" $PartitionStyle = 'GPT' } else { diff --git a/OSDCloud/private/steps/3-preinstall/step-preinstall-enablehighperformance.ps1 b/OSDCloud/private/steps/3-preinstall/step-preinstall-enablehighperformance.ps1 index ade6e95..6cab71c 100644 --- a/OSDCloud/private/steps/3-preinstall/step-preinstall-enablehighperformance.ps1 +++ b/OSDCloud/private/steps/3-preinstall/step-preinstall-enablehighperformance.ps1 @@ -5,7 +5,7 @@ function step-preinstall-enablehighperformance { Write-Verbose -Message "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" $Step = $global:OSDCloudCurrentStep #================================================= - if ($global:OSDCloudDevice.IsOnBattery -eq $true) { + if ($global:OSDCoreDevice.IsOnBattery -eq $true) { $classWin32Battery = (Get-CimInstance -ClassName Win32_Battery -ErrorAction SilentlyContinue | Select-Object -Property *) if ($classWin32Battery.BatteryStatus -eq 1) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Device has $($classWin32Battery.EstimatedChargeRemaining)% battery remaining" diff --git a/OSDCloud/private/steps/5-drivers/step-Add-WindowsDriver-OSDFolder.ps1 b/OSDCloud/private/steps/5-drivers/step-Add-WindowsDriver-OSDFolder.ps1 index 5af5e23..b8c5898 100644 --- a/OSDCloud/private/steps/5-drivers/step-Add-WindowsDriver-OSDFolder.ps1 +++ b/OSDCloud/private/steps/5-drivers/step-Add-WindowsDriver-OSDFolder.ps1 @@ -9,9 +9,9 @@ function step-Add-WindowsDriver-OSDFolder { $LogPath = "C:\Windows\Temp\osdcloud-logs" $OfflinePath = "C:\" - $osdManufacturer = $global:OSDCloudDevice.OSDManufacturer - $osdModel = $global:OSDCloudDevice.OSDModel - $osdProduct = $global:OSDCloudDevice.OSDProduct + $osdManufacturer = $global:OSDCoreDevice.OSDManufacturer + $osdModel = $global:OSDCoreDevice.OSDModel + $osdProduct = $global:OSDCoreDevice.OSDProduct if (-not (Test-Path -Path $LogPath)) { New-Item -ItemType Directory -Path $LogPath -Force | Out-Null diff --git a/OSDCloud/private/steps/5-drivers/step-Save-WindowsDriver-Firmware.ps1 b/OSDCloud/private/steps/5-drivers/step-Save-WindowsDriver-Firmware.ps1 index 324bce7..3f50e46 100644 --- a/OSDCloud/private/steps/5-drivers/step-Save-WindowsDriver-Firmware.ps1 +++ b/OSDCloud/private/steps/5-drivers/step-Save-WindowsDriver-Firmware.ps1 @@ -18,7 +18,7 @@ function step-Save-WindowsDriver-Firmware { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Microsoft Update Firmware is not enabled for Virtual Machines. Skip." return } - if ($global:OSDCloudDevice.IsOnBattery -eq $true) { + if ($global:OSDCoreDevice.IsOnBattery -eq $true) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Microsoft Update Firmware is not enabled for devices on battery power" return } @@ -56,7 +56,7 @@ function step-Save-WindowsDriver-Firmware { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Not all systems support a driver Firmware Update" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] BIOS or Firmware Settings may need to be enabled for Firmware Updates" - $SystemFirmwareHardwareId = $global:OSDCloudDevice.SystemFirmwareHardwareId + $SystemFirmwareHardwareId = $global:OSDCoreDevice.SystemFirmwareHardwareId Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] System Firmware Hardware ID: $SystemFirmwareHardwareId" Save-MicrosoftUpdateCatalogDriver -DestinationDirectory $DestinationDirectory -HardwareID $SystemFirmwareHardwareId diff --git a/OSDCloud/private/steps/5-drivers/step-Save-WindowsDriver-MSUpdate.ps1 b/OSDCloud/private/steps/5-drivers/step-Save-WindowsDriver-MSUpdate.ps1 index 98e5a5e..190dd93 100644 --- a/OSDCloud/private/steps/5-drivers/step-Save-WindowsDriver-MSUpdate.ps1 +++ b/OSDCloud/private/steps/5-drivers/step-Save-WindowsDriver-MSUpdate.ps1 @@ -14,7 +14,7 @@ function step-Save-WindowsDriver-MSUpdate { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] PowerShell 5.1 is required to run this step. Skip." return } - if (($IsVM -eq $true) -and ($global:OSDCloudDevice.OSDManufacturer -match 'Microsoft')) { + if (($IsVM -eq $true) -and ($global:OSDCoreDevice.OSDManufacturer -match 'Microsoft')) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Microsoft Update Drivers is not enabled for Microsoft Hyper-V. Skip." return } diff --git a/OSDCloud/private/workflow/Invoke-OSDCloudWorkflowTask.ps1 b/OSDCloud/private/workflow/Invoke-OSDCloudWorkflowTask.ps1 index 8076e90..585921a 100644 --- a/OSDCloud/private/workflow/Invoke-OSDCloudWorkflowTask.ps1 +++ b/OSDCloud/private/workflow/Invoke-OSDCloudWorkflowTask.ps1 @@ -15,9 +15,9 @@ function Invoke-OSDCloudWorkflowTask { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] ModuleVersion: $ModuleVersion" #================================================= # Set global variables - [System.String]$global:Architecture = $OSDCloudDevice.ProcessorArchitecture - [System.Boolean]$global:IsOnBattery = $OSDCloudDevice.IsOnBattery - [System.Boolean]$global:IsVM = $OSDCloudDevice.IsVM + [System.String]$global:Architecture = $OSDCoreDevice.ProcessorArchitecture + [System.Boolean]$global:IsOnBattery = $OSDCoreDevice.IsOnBattery + [System.Boolean]$global:IsVM = $OSDCoreDevice.IsVM [System.Boolean]$global:IsWinPE = $($env:SystemDrive -eq 'X:') #================================================= # Re-apply the OSDCloud Property override layer to $global:OSDCloudDeploy so authoritative @@ -30,9 +30,9 @@ function Invoke-OSDCloudWorkflowTask { $global:OSDCloudWorkflowInvoke = $null $global:OSDCloudWorkflowInvoke = [ordered]@{ Architecture = $global:Architecture - OSDManufacturer = $OSDCloudDevice.OSDManufacturer - OSDModel = $OSDCloudDevice.OSDModel - OSDProduct = $OSDCloudDevice.OSDProduct + OSDManufacturer = $OSDCoreDevice.OSDManufacturer + OSDModel = $OSDCoreDevice.OSDModel + OSDProduct = $OSDCoreDevice.OSDProduct DriverPackName = $global:OSDCloudDeploy.DriverPackName DriverPackObject = $global:OSDCloudDeploy.DriverPackObject DriverFolderName = $global:OSDCloudDeploy.DriverFolderName @@ -90,7 +90,7 @@ function Invoke-OSDCloudWorkflowTask { } } # UUID - $deviceUUID = $global:OSDCloudDevice.UUID + $deviceUUID = $global:OSDCoreDevice.UUID # Convert the UUID to a hash value to protect user privacyand ensure a consistent identifier across events $deviceUUIDHash = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($deviceUUID))).Replace("-", "") [string]$distinctId = $deviceUUIDHash @@ -109,19 +109,19 @@ function Invoke-OSDCloudWorkflowTask { } $eventProperties = @{ deploymentPhase = [string]$deploymentPhase - osdManufacturer = $OSDCloudDevice.OSDManufacturer - osdModel = $OSDCloudDevice.OSDModel - osdProduct = $OSDCloudDevice.OSDProduct - deviceManufacturer = $OSDCloudDevice.ComputerManufacturer - deviceModel = $OSDCloudDevice.ComputerModel - deviceSystemFamily = $OSDCloudDevice.ComputerSystemFamily - deviceSystemProduct = $OSDCloudDevice.ComputerSystemProduct - deviceSystemSKU = $OSDCloudDevice.ComputerSystemSKU - deviceSystemType = $OSDCloudDevice.ComputerSystemType - biosReleaseDate = $OSDCloudDevice.BiosReleaseDate - biosSMBIOSBIOSVersion = $OSDCloudDevice.BiosSMBIOSBIOSVersion - keyboardName = $OSDCloudDevice.KeyboardName - keyboardLayout = $OSDCloudDevice.KeyboardLayout + osdManufacturer = $OSDCoreDevice.OSDManufacturer + osdModel = $OSDCoreDevice.OSDModel + osdProduct = $OSDCoreDevice.OSDProduct + deviceManufacturer = $OSDCoreDevice.ComputerManufacturer + deviceModel = $OSDCoreDevice.ComputerModel + deviceSystemFamily = $OSDCoreDevice.ComputerSystemFamily + deviceSystemProduct = $OSDCoreDevice.ComputerSystemProduct + deviceSystemSKU = $OSDCoreDevice.ComputerSystemSKU + deviceSystemType = $OSDCoreDevice.ComputerSystemType + biosReleaseDate = $OSDCoreDevice.BiosReleaseDate + biosSMBIOSBIOSVersion = $OSDCoreDevice.BiosSMBIOSBIOSVersion + keyboardName = $OSDCoreDevice.KeyboardName + keyboardLayout = $OSDCoreDevice.KeyboardLayout winArchitecture = [string]$env:PROCESSOR_ARCHITECTURE winBuildLabEx = [string]$computerInfo.WindowsBuildLabEx winBuildNumber = [string]$computerInfo.OsBuildNumber diff --git a/OSDCloud/public/WinOS/Show-OSDCloudDeviceInfo.ps1 b/OSDCloud/public/WinOS/Show-OSDCloudDeviceInfo.ps1 index e555625..86e5516 100644 --- a/OSDCloud/public/WinOS/Show-OSDCloudDeviceInfo.ps1 +++ b/OSDCloud/public/WinOS/Show-OSDCloudDeviceInfo.ps1 @@ -41,7 +41,7 @@ Log Files Created: Functions Called: - Get-OSDCloudModuleVersion: Retrieves current OSDCloud module version -- Initialize-OSDCloudDevice: Populates $global:OSDCloudDevice with hardware details +- Initialize-OSDCoreDevice: Populates $global:OSDCoreDevice with hardware details The function updates the window title to '[OSDCloud] - WinPE and Device Information' to indicate the current operation status. #> @@ -55,7 +55,7 @@ function Show-OSDCloudDeviceInfo { #================================================= $OSDCloudModuleVersion = (Get-OSDCloudModuleVersion).ToString() Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] OSDCloud PowerShell Module $OSDCloudModuleVersion" - Initialize-OSDCloudDevice + Initialize-OSDCoreDevice #================================================= # Create the log path if it does not already exist $logsPath = Join-Path -Path $env:TEMP -ChildPath 'osdcloud-logs' @@ -70,10 +70,10 @@ function Show-OSDCloudDeviceInfo { #================================================= # Device Details Write-Host -ForegroundColor DarkGray "WinPE $($operatingSystem.Version) $($operatingSystem.OSArchitecture) $($computerSystem.Name)" - Write-Host -ForegroundColor DarkGray "Manufacturer: $($global:OSDCloudDevice.OSDManufacturer)" - Write-Host -ForegroundColor DarkGray "Model: $($global:OSDCloudDevice.OSDModel)" - Write-Host -ForegroundColor DarkGray "BIOS: $($global:OSDCloudDevice.BiosVersion)" - Write-Host -ForegroundColor DarkGray "BIOS Release Date: $($global:OSDCloudDevice.BiosReleaseDate)" + Write-Host -ForegroundColor DarkGray "Manufacturer: $($global:OSDCoreDevice.OSDManufacturer)" + Write-Host -ForegroundColor DarkGray "Model: $($global:OSDCoreDevice.OSDModel)" + Write-Host -ForegroundColor DarkGray "BIOS: $($global:OSDCoreDevice.BiosVersion)" + Write-Host -ForegroundColor DarkGray "BIOS Release Date: $($global:OSDCoreDevice.BiosReleaseDate)" foreach ($item in $processors) { Write-Host -ForegroundColor DarkGray "Processor: $($item.Name) [$($item.NumberOfLogicalProcessors) Logical]" diff --git a/OSDCloud/workflow/classic/ux/MainWindow.ps1 b/OSDCloud/workflow/classic/ux/MainWindow.ps1 index bfec6be..8bc74db 100644 --- a/OSDCloud/workflow/classic/ux/MainWindow.ps1 +++ b/OSDCloud/workflow/classic/ux/MainWindow.ps1 @@ -249,7 +249,7 @@ function Show-PowershellWindow() { #endregion #================================================= # Tpm -if ($global:OSDCloudDevice.IsTpmSpec -eq $true) { +if ($global:OSDCoreDevice.IsTpmSpec -eq $true) { $formMainWindowControlIsTpmSpec.Foreground = 'Green' } else { @@ -257,7 +257,7 @@ else { } # Autopilot -if ($global:OSDCloudDevice.IsAutopilotSpec -eq $true) { +if ($global:OSDCoreDevice.IsAutopilotSpec -eq $true) { $formMainWindowControlIsAutopilotSpec.Foreground = 'Green' } else { @@ -536,7 +536,7 @@ $formMainWindowControlStartButton.add_Click( #region Customizations #TODO fix the Version since this is not a Module function it doesn't give a version $ModuleVersion = $($MyInvocation.MyCommand.Module.Version) -$formMainWindow.Title = "OSDCloud on $($global:OSDCloudDevice.OSDManufacturer) $($global:OSDCloudDevice.OSDModel)" +$formMainWindow.Title = "OSDCloud on $($global:OSDCoreDevice.OSDManufacturer) $($global:OSDCoreDevice.OSDModel)" #endregion #================================================ #region Branding @@ -572,4 +572,4 @@ $jobCleanup.Flag = $false #Stop Cleaning Jobs $jobCleanup.PowerShell.Runspace.Close() #Close the runspace $jobCleanup.PowerShell.Dispose() #Remove the runspace from memory #endregion -#================================================ \ No newline at end of file +#================================================ diff --git a/OSDCloud/workflow/default/ux/MainWindow.ps1 b/OSDCloud/workflow/default/ux/MainWindow.ps1 index 1481ef4..95ead60 100644 --- a/OSDCloud/workflow/default/ux/MainWindow.ps1 +++ b/OSDCloud/workflow/default/ux/MainWindow.ps1 @@ -8,17 +8,17 @@ param() Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase #================================================ # Variables -$deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate -$deviceBiosVersion = $global:OSDCloudDevice.BiosVersion -$deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer -$deviceOSDModel = $global:OSDCloudDevice.OSDModel -$deviceOSDProduct = $global:OSDCloudDevice.OSDProduct -$deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU -$deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec -$deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec -$deviceSerialNumber = $global:OSDCloudDevice.SerialNumber -$deviceUUID = $global:OSDCloudDevice.UUID -$deviceHardwareHash = $global:OSDCloudDevice.HardwareHash +$deviceBiosReleaseDate = $global:OSDCoreDevice.BiosReleaseDate +$deviceBiosVersion = $global:OSDCoreDevice.BiosVersion +$deviceOSDManufacturer = $global:OSDCoreDevice.OSDManufacturer +$deviceOSDModel = $global:OSDCoreDevice.OSDModel +$deviceOSDProduct = $global:OSDCoreDevice.OSDProduct +$deviceComputerSystemSKU = $global:OSDCoreDevice.ComputerSystemSKU +$deviceIsAutopilotSpec = $global:OSDCoreDevice.IsAutopilotSpec +$deviceIsTpmSpec = $global:OSDCoreDevice.IsTpmSpec +$deviceSerialNumber = $global:OSDCoreDevice.SerialNumber +$deviceUUID = $global:OSDCoreDevice.UUID +$deviceHardwareHash = $global:OSDCoreDevice.HardwareHash $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # XAML diff --git a/OSDCloud/workflow/dev-alpha/ux/MainWindow.ps1 b/OSDCloud/workflow/dev-alpha/ux/MainWindow.ps1 index e3f138e..5bc9008 100644 --- a/OSDCloud/workflow/dev-alpha/ux/MainWindow.ps1 +++ b/OSDCloud/workflow/dev-alpha/ux/MainWindow.ps1 @@ -8,16 +8,16 @@ param() Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase #================================================ # Variables -$deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate -$deviceBiosVersion = $global:OSDCloudDevice.BiosVersion -$deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer -$deviceOSDModel = $global:OSDCloudDevice.OSDModel -$deviceOSDProduct = $global:OSDCloudDevice.OSDProduct -$deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU -$deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec -$deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec -$deviceSerialNumber = $global:OSDCloudDevice.SerialNumber -$deviceUUID = $global:OSDCloudDevice.UUID +$deviceBiosReleaseDate = $global:OSDCoreDevice.BiosReleaseDate +$deviceBiosVersion = $global:OSDCoreDevice.BiosVersion +$deviceOSDManufacturer = $global:OSDCoreDevice.OSDManufacturer +$deviceOSDModel = $global:OSDCoreDevice.OSDModel +$deviceOSDProduct = $global:OSDCoreDevice.OSDProduct +$deviceComputerSystemSKU = $global:OSDCoreDevice.ComputerSystemSKU +$deviceIsAutopilotSpec = $global:OSDCoreDevice.IsAutopilotSpec +$deviceIsTpmSpec = $global:OSDCoreDevice.IsTpmSpec +$deviceSerialNumber = $global:OSDCoreDevice.SerialNumber +$deviceUUID = $global:OSDCoreDevice.UUID $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # XAML @@ -419,8 +419,8 @@ $deviceUUIDText.Text = $deviceUUID <# $deviceTotalMemoryText = $window.FindName("deviceTotalMemoryText") -$deviceTotalMemoryText.Text = if ($global:OSDCloudDevice.TotalPhysicalMemoryGB) { - "$($global:OSDCloudDevice.TotalPhysicalMemoryGB) GB" +$deviceTotalMemoryText.Text = if ($global:OSDCoreDevice.TotalPhysicalMemoryGB) { + "$($global:OSDCoreDevice.TotalPhysicalMemoryGB) GB" } else { 'Unknown' } diff --git a/OSDCloud/workflow/dev-beta/ux/MainWindow.ps1 b/OSDCloud/workflow/dev-beta/ux/MainWindow.ps1 index 6eb211a..190971d 100644 --- a/OSDCloud/workflow/dev-beta/ux/MainWindow.ps1 +++ b/OSDCloud/workflow/dev-beta/ux/MainWindow.ps1 @@ -8,16 +8,16 @@ param() Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase #================================================ # Variables -$deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate -$deviceBiosVersion = $global:OSDCloudDevice.BiosVersion -$deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer -$deviceOSDModel = $global:OSDCloudDevice.OSDModel -$deviceOSDProduct = $global:OSDCloudDevice.OSDProduct -$deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU -$deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec -$deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec -$deviceSerialNumber = $global:OSDCloudDevice.SerialNumber -$deviceUUID = $global:OSDCloudDevice.UUID +$deviceBiosReleaseDate = $global:OSDCoreDevice.BiosReleaseDate +$deviceBiosVersion = $global:OSDCoreDevice.BiosVersion +$deviceOSDManufacturer = $global:OSDCoreDevice.OSDManufacturer +$deviceOSDModel = $global:OSDCoreDevice.OSDModel +$deviceOSDProduct = $global:OSDCoreDevice.OSDProduct +$deviceComputerSystemSKU = $global:OSDCoreDevice.ComputerSystemSKU +$deviceIsAutopilotSpec = $global:OSDCoreDevice.IsAutopilotSpec +$deviceIsTpmSpec = $global:OSDCoreDevice.IsTpmSpec +$deviceSerialNumber = $global:OSDCoreDevice.SerialNumber +$deviceUUID = $global:OSDCoreDevice.UUID $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # XAML @@ -375,8 +375,8 @@ $deviceUUIDText.Text = $deviceUUID <# $deviceTotalMemoryText = $window.FindName("deviceTotalMemoryText") -$deviceTotalMemoryText.Text = if ($global:OSDCloudDevice.TotalPhysicalMemoryGB) { - "$($global:OSDCloudDevice.TotalPhysicalMemoryGB) GB" +$deviceTotalMemoryText.Text = if ($global:OSDCoreDevice.TotalPhysicalMemoryGB) { + "$($global:OSDCoreDevice.TotalPhysicalMemoryGB) GB" } else { 'Unknown' } diff --git a/OSDCloud/workflow/dev-device/ux/MainWindow.ps1 b/OSDCloud/workflow/dev-device/ux/MainWindow.ps1 index e16bf7e..683f5d8 100644 --- a/OSDCloud/workflow/dev-device/ux/MainWindow.ps1 +++ b/OSDCloud/workflow/dev-device/ux/MainWindow.ps1 @@ -8,17 +8,17 @@ param() Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase #================================================ # Variables -$deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate -$deviceBiosVersion = $global:OSDCloudDevice.BiosVersion -$deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer -$deviceOSDModel = $global:OSDCloudDevice.OSDModel -$deviceOSDProduct = $global:OSDCloudDevice.OSDProduct -$deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU -$deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec -$deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec -$deviceSerialNumber = $global:OSDCloudDevice.SerialNumber -$deviceUUID = $global:OSDCloudDevice.UUID -$deviceHardwareHash = $global:OSDCloudDevice.HardwareHash +$deviceBiosReleaseDate = $global:OSDCoreDevice.BiosReleaseDate +$deviceBiosVersion = $global:OSDCoreDevice.BiosVersion +$deviceOSDManufacturer = $global:OSDCoreDevice.OSDManufacturer +$deviceOSDModel = $global:OSDCoreDevice.OSDModel +$deviceOSDProduct = $global:OSDCoreDevice.OSDProduct +$deviceComputerSystemSKU = $global:OSDCoreDevice.ComputerSystemSKU +$deviceIsAutopilotSpec = $global:OSDCoreDevice.IsAutopilotSpec +$deviceIsTpmSpec = $global:OSDCoreDevice.IsTpmSpec +$deviceSerialNumber = $global:OSDCoreDevice.SerialNumber +$deviceUUID = $global:OSDCoreDevice.UUID +$deviceHardwareHash = $global:OSDCoreDevice.HardwareHash $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # XAML diff --git a/OSDCloud/workflow/insiders/ux/MainWindow.ps1 b/OSDCloud/workflow/insiders/ux/MainWindow.ps1 index 33fefe2..e3f418d 100644 --- a/OSDCloud/workflow/insiders/ux/MainWindow.ps1 +++ b/OSDCloud/workflow/insiders/ux/MainWindow.ps1 @@ -8,16 +8,16 @@ param() Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase #================================================ # Variables -$deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate -$deviceBiosVersion = $global:OSDCloudDevice.BiosVersion -$deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer -$deviceOSDModel = $global:OSDCloudDevice.OSDModel -$deviceOSDProduct = $global:OSDCloudDevice.OSDProduct -$deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU -$deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec -$deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec -$deviceSerialNumber = $global:OSDCloudDevice.SerialNumber -$deviceUUID = $global:OSDCloudDevice.UUID +$deviceBiosReleaseDate = $global:OSDCoreDevice.BiosReleaseDate +$deviceBiosVersion = $global:OSDCoreDevice.BiosVersion +$deviceOSDManufacturer = $global:OSDCoreDevice.OSDManufacturer +$deviceOSDModel = $global:OSDCoreDevice.OSDModel +$deviceOSDProduct = $global:OSDCoreDevice.OSDProduct +$deviceComputerSystemSKU = $global:OSDCoreDevice.ComputerSystemSKU +$deviceIsAutopilotSpec = $global:OSDCoreDevice.IsAutopilotSpec +$deviceIsTpmSpec = $global:OSDCoreDevice.IsTpmSpec +$deviceSerialNumber = $global:OSDCoreDevice.SerialNumber +$deviceUUID = $global:OSDCoreDevice.UUID $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # XAML diff --git a/OSDCloud/workflow/vNext-v1/ui/MainWindow.ps1 b/OSDCloud/workflow/vNext-v1/ui/MainWindow.ps1 index 548648d..c143d2b 100644 --- a/OSDCloud/workflow/vNext-v1/ui/MainWindow.ps1 +++ b/OSDCloud/workflow/vNext-v1/ui/MainWindow.ps1 @@ -21,17 +21,17 @@ try { } #================================================ # Variables -$deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate -$deviceBiosVersion = $global:OSDCloudDevice.BiosVersion -$deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer -$deviceOSDModel = $global:OSDCloudDevice.OSDModel -$deviceOSDProduct = $global:OSDCloudDevice.OSDProduct -$deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU -$deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec -$deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec -$deviceSerialNumber = $global:OSDCloudDevice.SerialNumber -$deviceUUID = $global:OSDCloudDevice.UUID -$deviceHardwareHash = $global:OSDCloudDevice.HardwareHash +$deviceBiosReleaseDate = $global:OSDCoreDevice.BiosReleaseDate +$deviceBiosVersion = $global:OSDCoreDevice.BiosVersion +$deviceOSDManufacturer = $global:OSDCoreDevice.OSDManufacturer +$deviceOSDModel = $global:OSDCoreDevice.OSDModel +$deviceOSDProduct = $global:OSDCoreDevice.OSDProduct +$deviceComputerSystemSKU = $global:OSDCoreDevice.ComputerSystemSKU +$deviceIsAutopilotSpec = $global:OSDCoreDevice.IsAutopilotSpec +$deviceIsTpmSpec = $global:OSDCoreDevice.IsTpmSpec +$deviceSerialNumber = $global:OSDCoreDevice.SerialNumber +$deviceUUID = $global:OSDCoreDevice.UUID +$deviceHardwareHash = $global:OSDCoreDevice.HardwareHash $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # WPFUI Fluent theme initialization diff --git a/OSDCloud/workflow/vNext-v2/ui/MainWindow.ps1 b/OSDCloud/workflow/vNext-v2/ui/MainWindow.ps1 index 548648d..c143d2b 100644 --- a/OSDCloud/workflow/vNext-v2/ui/MainWindow.ps1 +++ b/OSDCloud/workflow/vNext-v2/ui/MainWindow.ps1 @@ -21,17 +21,17 @@ try { } #================================================ # Variables -$deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate -$deviceBiosVersion = $global:OSDCloudDevice.BiosVersion -$deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer -$deviceOSDModel = $global:OSDCloudDevice.OSDModel -$deviceOSDProduct = $global:OSDCloudDevice.OSDProduct -$deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU -$deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec -$deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec -$deviceSerialNumber = $global:OSDCloudDevice.SerialNumber -$deviceUUID = $global:OSDCloudDevice.UUID -$deviceHardwareHash = $global:OSDCloudDevice.HardwareHash +$deviceBiosReleaseDate = $global:OSDCoreDevice.BiosReleaseDate +$deviceBiosVersion = $global:OSDCoreDevice.BiosVersion +$deviceOSDManufacturer = $global:OSDCoreDevice.OSDManufacturer +$deviceOSDModel = $global:OSDCoreDevice.OSDModel +$deviceOSDProduct = $global:OSDCoreDevice.OSDProduct +$deviceComputerSystemSKU = $global:OSDCoreDevice.ComputerSystemSKU +$deviceIsAutopilotSpec = $global:OSDCoreDevice.IsAutopilotSpec +$deviceIsTpmSpec = $global:OSDCoreDevice.IsTpmSpec +$deviceSerialNumber = $global:OSDCoreDevice.SerialNumber +$deviceUUID = $global:OSDCoreDevice.UUID +$deviceHardwareHash = $global:OSDCoreDevice.HardwareHash $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # WPFUI Fluent theme initialization diff --git a/OSDCloud/workflow/vNext/ui/MainWindow.ps1 b/OSDCloud/workflow/vNext/ui/MainWindow.ps1 index 7892e6d..943665c 100644 --- a/OSDCloud/workflow/vNext/ui/MainWindow.ps1 +++ b/OSDCloud/workflow/vNext/ui/MainWindow.ps1 @@ -21,17 +21,17 @@ try { } #================================================ # Variables -$deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate -$deviceBiosVersion = $global:OSDCloudDevice.BiosVersion -$deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer -$deviceOSDModel = $global:OSDCloudDevice.OSDModel -$deviceOSDProduct = $global:OSDCloudDevice.OSDProduct -$deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU -$deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec -$deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec -$deviceSerialNumber = $global:OSDCloudDevice.SerialNumber -$deviceUUID = $global:OSDCloudDevice.UUID -$deviceHardwareHash = $global:OSDCloudDevice.HardwareHash +$deviceBiosReleaseDate = $global:OSDCoreDevice.BiosReleaseDate +$deviceBiosVersion = $global:OSDCoreDevice.BiosVersion +$deviceOSDManufacturer = $global:OSDCoreDevice.OSDManufacturer +$deviceOSDModel = $global:OSDCoreDevice.OSDModel +$deviceOSDProduct = $global:OSDCoreDevice.OSDProduct +$deviceComputerSystemSKU = $global:OSDCoreDevice.ComputerSystemSKU +$deviceIsAutopilotSpec = $global:OSDCoreDevice.IsAutopilotSpec +$deviceIsTpmSpec = $global:OSDCoreDevice.IsTpmSpec +$deviceSerialNumber = $global:OSDCoreDevice.SerialNumber +$deviceUUID = $global:OSDCoreDevice.UUID +$deviceHardwareHash = $global:OSDCoreDevice.HardwareHash $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # WPFUI Fluent theme initialization @@ -422,7 +422,7 @@ Update-TaskSequenceSteps # Local ISO Values function Get-LocalIsoFile { $localIsoFiles = @( - Get-OSDCloudCache -Type ISO -ErrorAction SilentlyContinue | + Get-OSDCoreCacheContent -Type ISO -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'ISO' -and -not [string]::IsNullOrWhiteSpace([string]$_.FullName) } ) @@ -487,7 +487,7 @@ function Get-DriverPackCacheFile { function Get-DriverPackCacheItems { return @( - Get-OSDCloudCache -Type DriverPacks -ErrorAction SilentlyContinue | + Get-OSDCoreCacheContent -Type DriverPacks -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'DriverPacks' -and -not [string]::IsNullOrWhiteSpace([string]$_.FullName) } ) } @@ -533,7 +533,7 @@ function Get-DriverPackUsbCacheTarget { ) $cacheRoots = @( - Get-OSDCloudCache -ErrorAction SilentlyContinue | + Get-OSDCoreCacheContent -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'Cache' -and -not [string]::IsNullOrWhiteSpace([string]$_.DriveRoot) } ) @@ -657,7 +657,7 @@ function Get-CloudOperatingSystemFileName { function Get-CloudOperatingSystemCacheItems { return @( - Get-OSDCloudCache -Type ESD, WIM -ErrorAction SilentlyContinue | + Get-OSDCoreCacheContent -Type ESD, WIM -ErrorAction SilentlyContinue | Where-Object { $_.Type -in @('ESD', 'WIM') -and -not [string]::IsNullOrWhiteSpace([string]$_.FullName) } ) } @@ -744,7 +744,7 @@ function Get-DriveVolumeMetadata { } function Get-DriverFolderItem { - $driverFolders = Get-OSDCloudCache -Type Drivers -ErrorAction SilentlyContinue | + $driverFolders = Get-OSDCoreCacheContent -Type Drivers -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'Drivers' -and -not [string]::IsNullOrWhiteSpace([string]$_.FullName) } | ForEach-Object { $folderPath = [string]$_.FullName @@ -1017,9 +1017,9 @@ if (-not [string]::IsNullOrWhiteSpace([string]$deviceHardwareHash)) { }) } -$OSDCloudDeviceGrid = $window.FindName('OSDCloudDeviceGrid') +$OSDCoreDeviceGrid = $window.FindName('OSDCoreDeviceGrid') -function Convert-OSDCloudDeviceValueToString { +function Convert-OSDCoreDeviceValueToString { param($Value) if ($null -eq $Value) { @@ -1041,8 +1041,8 @@ function Convert-OSDCloudDeviceValueToString { return [string]$Value } -function Get-OSDCloudDeviceItems { - $deviceObject = $global:OSDCloudDevice +function Get-OSDCoreDeviceItems { + $deviceObject = $global:OSDCoreDevice if (-not $deviceObject) { return @() } @@ -1054,7 +1054,7 @@ function Get-OSDCloudDeviceItems { ForEach-Object { [PSCustomObject]@{ Key = [string]$_.Key - Value = Convert-OSDCloudDeviceValueToString -Value $_.Value + Value = Convert-OSDCoreDeviceValueToString -Value $_.Value } } ) @@ -1071,14 +1071,14 @@ function Get-OSDCloudDeviceItems { ForEach-Object { [PSCustomObject]@{ Key = [string]$_.Name - Value = Convert-OSDCloudDeviceValueToString -Value $_.Value + Value = Convert-OSDCoreDeviceValueToString -Value $_.Value } } ) } -if ($OSDCloudDeviceGrid) { - $OSDCloudDeviceGrid.ItemsSource = Get-OSDCloudDeviceItems +if ($OSDCoreDeviceGrid) { + $OSDCoreDeviceGrid.ItemsSource = Get-OSDCoreDeviceItems } #================================================ # Summary / Selected detail text blocks diff --git a/OSDCloud/workflow/vNext/ui/MainWindow.xaml b/OSDCloud/workflow/vNext/ui/MainWindow.xaml index 7bd76af..f0918dc 100644 --- a/OSDCloud/workflow/vNext/ui/MainWindow.xaml +++ b/OSDCloud/workflow/vNext/ui/MainWindow.xaml @@ -283,11 +283,11 @@ - - - + diff --git a/README.md b/README.md index ef68791..356a935 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Deploy-OSDCloud | Cmdlet | Description | |---|---| | `Deploy-OSDCloud` | Starts an OS deployment workflow (GUI or CLI mode). | -| `Get-OSDCloudCache` | Returns the module cache path used for downloaded content. | +| `Get-OSDCoreCacheContent` | Returns the module cache path used for downloaded content. | | `Get-OSDCloudModulePath` | Returns the module installation directory. | | `Get-OSDCloudModuleVersion` | Returns the loaded module version. | | `Show-OSDCloudDeviceInfo` | Displays device hardware and environment information. | @@ -85,7 +85,7 @@ Task-oriented guides for IT admins. Start at the [docs index](docs/README.md), o | Reference page | Function | |---|---| | [OSDCloud/docs/Deploy-OSDCloud.md](OSDCloud/docs/Deploy-OSDCloud.md) | `Deploy-OSDCloud` | -| [OSDCloud/docs/Get-OSDCloudCache.md](OSDCloud/docs/Get-OSDCloudCache.md) | `Get-OSDCloudCache` | +| [OSDCloud/docs/Get-OSDCoreCacheContent.md](OSDCloud/docs/Get-OSDCoreCacheContent.md) | `Get-OSDCoreCacheContent` | | [OSDCloud/docs/Get-OSDCloudModulePath.md](OSDCloud/docs/Get-OSDCloudModulePath.md) | `Get-OSDCloudModulePath` | | [OSDCloud/docs/Get-OSDCloudModuleVersion.md](OSDCloud/docs/Get-OSDCloudModuleVersion.md) | `Get-OSDCloudModuleVersion` | | [OSDCloud/docs/Show-OSDCloudDeviceInfo.md](OSDCloud/docs/Show-OSDCloudDeviceInfo.md) | `Show-OSDCloudDeviceInfo` |