Public/Functions/WindowsAdk.ps1

<#
.SYNOPSIS
Adds PowerShell and PowerShell Gallery support to ADK's x64 winpe.wim
 
.DESCRIPTION
Adds PowerShell and PowerShell Gallery support to ADK's x64 winpe.wim. This will speed things up with MDT and MEM CM going forward
 
.LINK
https://github.com/OSDeploy/OSD/tree/master/docs
 
.NOTES
21.3.15.2 Initial Release
#>

function Edit-AdkWinPEWIM {
<#
.SYNOPSIS
Edits AdkWinPEWIM content.
 
.DESCRIPTION
Applies modifications to AdkWinPEWIM in the current servicing workflow.
 
.EXAMPLE
Edit-AdkWinPEWIM
Demonstrates a common way to run Edit-AdkWinPEWIM.
 
.LINK
https://github.com/OSDeploy/OSD/tree/master/docs
 
.NOTES
Author: David Segura - Recast Software
2026-07-13 - Initial help block created
2026-07-13 - Refined generated help text
#>

    [CmdletBinding()]
    param ()

    #=================================================
    # Start the Clock
    #=================================================
    $StartTime = Get-Date
    #=================================================
    # Blocks
    #=================================================
    Block-WinPE
    $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity)
    if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required"
        return
    }
    Block-NoCurl
    #=================================================
    # Set VerbosePreference
    #=================================================
    $CurrentVerbosePreference = $VerbosePreference
    $VerbosePreference = 'Continue'
    #=================================================
    # Get ADK
    #=================================================
    $WinPEArch = 'amd64'
    $WindowsAdkPaths = Get-WindowsAdkPaths -Architecture $WinPEArch

    if ($null -eq $WindowsAdkPaths) {
        Write-Warning "Could not get ADK going, sorry"
        Break
    }
    #=================================================
    # Get WinPE.wim
    #=================================================
    $WimSourcePath = $WindowsAdkPaths.WimSourcePath
    if (-NOT (Test-Path $WimSourcePath)) {
        Write-Warning "Could not find $WimSourcePath, sorry"
        Break
    }
    $WimSourceItem = Get-Item $WimSourcePath
    #=================================================
    # Create Backup
    #=================================================
    if (-NOT (Test-Path "$($WimSourceItem.Directory)\winpe.bak")) {
        $WimSourceItem | Copy-Item -Destination "$($WimSourceItem.Directory)\winpe.bak" -Force -ErrorAction Stop
    }
    #=================================================
    # Mount-MyWindowsImage
    #=================================================
    $MountMyWindowsImage = Mount-MyWindowsImage $WimSourceItem
    $MountPath = $MountMyWindowsImage.Path
    #=================================================
    # Add Packages
    #=================================================
    $ErrorActionPreference = 'Ignore'
    $WinPEOCs = $WindowsAdkPaths.WinPEOCs

    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-WMI.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-WMI_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-HTA.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-HTA_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-NetFx.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-NetFx_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-Scripting.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-Scripting_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-PowerShell.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-PowerShell_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-SecureStartup.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-SecureStartup_en-us.cab"

    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-DismCmdlets.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-DismCmdlets_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-Dot3Svc.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-Dot3Svc_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-EnhancedStorage.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-EnhancedStorage_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-FMAPI.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-GamingPeripherals.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-PPPoE.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-PPPoE_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-PlatformId.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-PmemCmdlets.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-PmemCmdlets_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-RNDIS.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-RNDIS_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-SecureBootCmdlets.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-StorageWMI.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-StorageWMI_en-us.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\WinPE-WDS-Tools.cab"
    Add-WindowsPackage -Path $MountPath -PackagePath "$WinPEOCs\en-us\WinPE-WDS-Tools_en-us.cab"
    #=================================================
    # cURL
    #=================================================
    Write-Verbose "Adding curl.exe to $MountPath"
    if (Test-Path "$env:SystemRoot\System32\curl.exe") {
        robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" curl.exe /ndl /nfl /njh /njs /b
    } else {
        Write-Warning "Could not find $env:SystemRoot\System32\curl.exe"
        Write-Warning "You must be using an old version of Windows"
    }
    #=================================================
    # PowerShell Execution Policy
    #=================================================
    Write-Verbose "Setting PowerShell ExecutionPolicy to Bypass in $MountPath"
    Set-WindowsImageExecutionPolicy -Path $MountPath -ExecutionPolicy Bypass
    #=================================================
    # Enable PowerShell Gallery
    #=================================================
    Write-Verbose "Enabling PowerShell Gallery support in $MountPath"
    Enable-PEWindowsImagePSGallery -Path $MountPath

    #Write-Verbose "Saving OSD to $MountPath\Program Files\WindowsPowerShell\Modules"
    #Save-Module -Name OSD -Path "$MountPath\Program Files\WindowsPowerShell\Modules" -Force
    #=================================================
    # Startnet
    #=================================================
    #Write-Verbose "Adding PowerShell.exe to Startnet.cmd"
    #Add-Content -Path "$MountPath\Windows\System32\Startnet.cmd" -Value 'start powershell.exe' -Force
    #=================================================
    # DriverPath
    #=================================================
<# foreach ($Driver in $DriverPath) {
        Add-WindowsDriver -Path "$($MountMyWindowsImage.Path)" -Driver "$Driver" -Recurse -ForceUnsigned
    } #>

    #=================================================
    # Save WIM
    #=================================================
    $MountMyWindowsImage | Dismount-MyWindowsImage -Save
    #=================================================
    # Restore VerbosePreference
    #=================================================
    $VerbosePreference = $CurrentVerbosePreference
    #=================================================
    # Complete
    #=================================================
    $EndTime = Get-Date
    $TimeSpan = New-TimeSpan -Start $StartTime -End $EndTime
    Write-Host -ForegroundColor DarkGray    "================================================"
    Write-Host -ForegroundColor Yellow      "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) $($MyInvocation.MyCommand.Name) " -NoNewline
    Write-Host -ForegroundColor Cyan        "Completed in $($TimeSpan.ToString("mm' minutes 'ss' seconds'"))"
    #=================================================
}
function Get-WindowsAdkInstallPath {
    <#
    .SYNOPSIS
    Retrieves the installation path of the Windows Assessment and Deployment Kit (ADK)
 
    .DESCRIPTION
    Retrieves the installation path of the Windows Assessment and Deployment Kit (ADK) from the registry.
 
    .EXAMPLE
    Get-WindowsAdkInstallPath
    Returns the ADK installation directory path
 
    .NOTES
    Author: David Segura - Recast Software
    2026-07-10 - Updated help to follow OSD standard
 
    .LINK
    https://github.com/OSDeploy/OSD/tree/master/docs
    #>

    [CmdletBinding()]
    param ()

    $WindowsKitsInstallPath = Get-WindowsKitsInstallPath

    if ($WindowsKitsInstallPath) {
        $WindowsAdkInstallPath = Join-Path $WindowsKitsInstallPath 'Assessment and Deployment Kit'

        if (Test-Path "$WindowsAdkInstallPath") {
            Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Windows Assessment and Deployment Kit install path is $WindowsAdkInstallPath"
            return $WindowsAdkInstallPath
        }
        else {
            Write-Warning "[$(Get-Date -format s)] Windows Assessment and Deployment Kit is not installed"
            return $null
        }

    }
    else {
        Write-Warning "[$(Get-Date -format s)] Windows Assessment and Deployment Kit is not installed"
        return $null
    }
}
function Get-WindowsAdkInstallVersion {
    <#
    .SYNOPSIS
    Retrieves the installed version of the Windows Assessment and Deployment Kit (ADK)
 
    .DESCRIPTION
    Retrieves the installed version of the Windows Assessment and Deployment Kit (ADK) from the registry.
 
    .EXAMPLE
    Get-WindowsAdkInstallVersion
    Returns the ADK version string
 
    .NOTES
    Author: David Segura - Recast Software
    2026-07-10 - Updated help to follow OSD standard
 
    .LINK
    https://github.com/OSDeploy/OSD/tree/master/docs
    #>

    [CmdletBinding()]
    param ()

    $Result = Get-ItemProperty HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* -ErrorAction SilentlyContinue | `
    Where-Object { $_.DisplayName -eq 'Windows Assessment and Deployment Kit' } | Select-Object -ExpandProperty DisplayVersion -First 1

    return $Result
}
function Get-WindowsAdkPaths {
    <#
    .SYNOPSIS
    Retrieves the command paths of the Windows Assessment and Deployment Kit (ADK).
 
    .DESCRIPTION
    Retrieves the command paths of the Windows Assessment and Deployment Kit (ADK).
 
    .NOTES
    Author: David Segura
    #>

    [CmdletBinding()]
    param (
        [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true)]
        # Windows ADK architecture to get. Valid values are 'amd64', 'x86', and 'arm64'.
        [ValidateSet('amd64', 'x86', 'arm64')]
        [Alias('Arch')]
        [string]$Architecture = $Env:PROCESSOR_ARCHITECTURE,

        [Parameter(Position = 1, ValueFromPipelineByPropertyName = $true)]
        # Path to the Windows ADK root directory. Typically 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit'
        <#
        [ValidateScript({
            if (!($_ | Test-Path)) {
                throw 'Path does not exist'
            }
            if (!($_ | Test-Path -PathType Container)) {
                throw 'Path must be a directory'
            }
            if (!(Test-Path "$_\Deployment Tools")) {
                throw 'Path does not contain a Deployment Tools directory'
            }
            if (!(Test-Path "$_\Windows Preinstallation Environment")) {
                throw 'Path does not contain a Windows Preinstallation Environment directory'
            }
            return $true
        })]
        #>

        [Alias('AdkRoot')]
        $WindowsAdkRoot
    )
    #=================================================
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start"
    $Error.Clear()
    #=================================================
    # region Get Windows ADK information from the Registry
    if (-not $WindowsAdkRoot) {
        $DefaultPath = "${env:ProgramFiles(x86)}\Windows Kits\10"
        $InstalledRoots32 = 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots'
        $InstalledRoots64 = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots'
        $RegistryValue = 'KitsRoot10'
        $KitsRoot10 = $null

        if (Test-Path -Path $InstalledRoots64) {
            $RegistryKey = Get-Item -Path $InstalledRoots64
            if ($null -ne $RegistryKey.GetValue($RegistryValue)) {
                $KitsRoot10 = Get-ItemPropertyValue -Path $InstalledRoots64 -Name $RegistryValue -ErrorAction SilentlyContinue

                if (Test-Path -Path $KitsRoot10) {
                    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found KitsRoot10 in $InstalledRoots64"
                }
                else {
                    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] KitsRoot10 path from registry does not exist: $KitsRoot10"
                    $KitsRoot10 = $null
                }
            }
            else {
                Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Registry value $RegistryValue not found in $InstalledRoots64"
            }
        }

        if (-NOT ($KitsRoot10)) {
            if (Test-Path -Path $DefaultPath) {
                Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found KitsRoot10 in $DefaultPath"
                $KitsRoot10 = $DefaultPath
            }
        }

        if (-NOT ($KitsRoot10)) {
            if (Test-Path -Path $InstalledRoots32) {
                $RegistryKey = Get-Item -Path $InstalledRoots32
                if ($null -ne $RegistryKey.GetValue($RegistryValue)) {
                    $KitsRoot10 = Get-ItemPropertyValue -Path $InstalledRoots32 -Name $RegistryValue -ErrorAction SilentlyContinue
                }
                else {
                    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Registry value $RegistryValue not found in $InstalledRoots32"
                }
            }
        }

        Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] $KitsRoot10"

        if ($KitsRoot10) {
            $WindowsAdkRoot = Join-Path $KitsRoot10 'Assessment and Deployment Kit'
        }
        else {
            Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Unable to determine ADK Path"
            return
        }
    }
    #endregion
    #=================================================
    # region Validate Windows ADK Path
    $WinPERoot = Join-Path $WindowsAdkRoot 'Windows Preinstallation Environment'
    if (-NOT (Test-Path $WinPERoot -PathType Container)) {
        Write-Warning "[$(Get-Date -format s)] WinPERoot is not a valid path $WinPERoot"
        $WinPERoot = $null
        return
    }
    #endregion
    #=================================================
    # region Set ADK Paths
    $PathDeploymentTools = Join-Path $WindowsAdkRoot (Join-Path 'Deployment Tools' $Architecture)
    $PathWinPE = Join-Path $WinPERoot $Architecture
    #endregion
    #=================================================
    # region Build Results
    $Results = [PSCustomObject] @{
        #KitsRoot = $KitsRoot10
        AdkRoot             = $WindowsAdkRoot
        PathBCDBoot         = Join-Path $PathDeploymentTools 'BCDBoot'
        PathDeploymentTools = $PathDeploymentTools
        PathDISM            = Join-Path $PathDeploymentTools 'DISM'
        PathOscdimg         = Join-Path $PathDeploymentTools 'Oscdimg'
        PathUsmt            = Join-Path $WindowsAdkRoot (Join-Path 'User State Migration Tool' $Architecture)
        PathWinPE           = Join-Path $WinPERoot $Architecture
        PathWinPEMedia      = Join-Path $PathWinPE 'Media'
        PathWinSetup        = Join-Path $WindowsAdkRoot (Join-Path 'Windows Setup' $Architecture)
        WinPEOCs            = Join-Path $PathWinPE 'WinPE_OCs'
        WinPERoot           = $WinPERoot
        WimSourcePath       = Join-Path $PathWinPE 'en-us\winpe.wim'

        bcdbootexe          = Join-Path $PathDeploymentTools (Join-Path 'BCDBoot' 'bcdboot.exe')
        bcdeditexe          = Join-Path $PathDeploymentTools (Join-Path 'BCDBoot' 'bcdedit.exe')
        bootsectexe         = Join-Path $PathDeploymentTools (Join-Path 'BCDBoot' 'bootsect.exe')
        dismexe             = Join-Path $PathDeploymentTools (Join-Path 'DISM' 'dism.exe')
        efisysbin           = Join-Path $PathDeploymentTools (Join-Path 'Oscdimg' 'efisys.bin')
        efisysnopromptbin   = Join-Path $PathDeploymentTools (Join-Path 'Oscdimg' 'efisys_noprompt.bin')
        etfsbootcom         = if ($Architecture -eq 'arm64') {
            # ARM64 does not have a etfsboot.com | Redirect to amd64 folder
            Join-Path (Join-Path $WindowsAdkRoot (Join-Path 'Deployment Tools' 'amd64')) (Join-Path 'Oscdimg' 'etfsboot.com')
        }
        else {
            Join-Path $PathDeploymentTools (Join-Path 'Oscdimg' 'etfsboot.com')
        }
        imagexexe           = Join-Path $PathDeploymentTools (Join-Path 'DISM' 'imagex.exe')
        oa3toolexe          = Join-Path $PathDeploymentTools (Join-Path 'Licensing\OA30' 'oa3tool.exe')
        oscdimgexe          = Join-Path $PathDeploymentTools (Join-Path 'Oscdimg' 'oscdimg.exe')
        pkgmgrexe           = Join-Path $PathDeploymentTools (Join-Path 'DISM' 'pkgmgr.exe')
    }
    Return $Results
    #endregion
    #=================================================
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End"
    #=================================================
}
function Get-WindowsKitsInstallPath {
    <#
    .SYNOPSIS
    Retrieves the installation path of the Windows Kit directory.
 
    .DESCRIPTION
    Retrieves the installation path of the Windows Kits (which includes ADK and other Windows development tools) from the registry.
 
    .EXAMPLE
    Get-WindowsKitsInstallPath
    Returns the Windows Kits installation directory path
 
    .NOTES
    Author: David Segura - Recast Software
    2026-07-10 - Updated help to follow OSD standard
 
    .LINK
    https://github.com/OSDeploy/OSD/tree/master/docs
    #>

    [CmdletBinding()]
    param ()

    # 32-bit Registry
    $InstalledRoots32 = 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots'
    # 64-bit Registry
    $InstalledRoots64 = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots'

    $RegistryValue = 'KitsRoot10'
    $KitsRoot10 = $null

    # Test for 64-bit Registry
    if (Test-Path -Path $InstalledRoots64) {
        $RegistryKey = Get-Item -Path $InstalledRoots64
        if ($null -ne $RegistryKey.GetValue($RegistryValue)) {
            $KitsRoot10 = Get-ItemPropertyValue -Path $InstalledRoots64 -Name $RegistryValue -ErrorAction SilentlyContinue
        }
    }

    if (-not ($KitsRoot10)) {
        if (Test-Path -Path $InstalledRoots32) {
            $RegistryKey = Get-Item -Path $InstalledRoots32
            if ($null -ne $RegistryKey.GetValue($RegistryValue)) {
                $KitsRoot10 = Get-ItemPropertyValue -Path $InstalledRoots32 -Name $RegistryValue -ErrorAction SilentlyContinue
            }
        }
    }


    if ($KitsRoot10) {
        if (Test-Path "$KitsRoot10") {
            Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Windows Kits install path is $KitsRoot10"
            return $KitsRoot10
        }
        else {
            Write-Warning "[$(Get-Date -format s)] Windows Kits install path from the registry does not exist at $KitsRoot10"
            return $null
        }
    }
    else {
        Write-Warning "[$(Get-Date -format s)] Windows Kits is not installed"
        return $null
    }
}
<#
.SYNOPSIS
Creates an ADK CopyPE working directory
 
.DESCRIPTION
Creates a working directory structure for ADK CopyPE media with bootable WinPE environment.
 
.PARAMETER MediaPath
Path where the CopyPE working directory will be created. This parameter is mandatory.
 
.EXAMPLE
New-AdkCopyPE -MediaPath 'C:\CopyPEMedia'
Creates a CopyPE working directory at C:\CopyPEMedia
 
.NOTES
Author: David Segura - Recast Software
2026-07-10 - Updated help to follow OSD standard
2021-05-27 - Resolved issue with paths
2021-03-15 - Renamed to make it easier to understand what it does
2021-03-10 - Initial Release
 
.LINK
https://github.com/OSDeploy/OSD/tree/master/docs
#>

function New-AdkCopyPE {
<#
.SYNOPSIS
Creates AdkCopyPE resources.
 
.DESCRIPTION
Builds new AdkCopyPE resources based on the provided parameters.
 
.PARAMETER Path
Specifies the Path to use when running New-AdkCopyPE.
 
.PARAMETER WinPEArch
Specifies the WinPEArch to use when running New-AdkCopyPE.
 
.EXAMPLE
New-AdkCopyPE -Path <value>
Demonstrates a common way to run New-AdkCopyPE.
 
.LINK
https://github.com/OSDeploy/OSD/tree/master/docs
 
.NOTES
Author: David Segura - Recast Software
2026-07-13 - Initial help block created
2026-07-13 - Refined generated help text
#>

    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$Path,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateSet('amd64','x86','arm64')]
        [string]$WinPEArch = 'amd64'
    )

    #=================================================
    # Require Admin Rights
    #=================================================
    if ((Get-OSDGather -Property IsAdmin) -eq $false) {
        Write-Warning "$($MyInvocation.MyCommand) requires Admin Rights ELEVATED"
        Break
    }
    #=================================================
    # Get Adk Paths
    #=================================================
    $WindowsAdkPaths = Get-WindowsAdkPaths -Architecture $WinPEArch
    #=================================================
    $Destination = $Path

    $AdkWimSourcePath = $WindowsAdkPaths.WimSourcePath
    $AdkPathOscdimg = $WindowsAdkPaths.PathOscdimg
    $AdkPathWinPEMedia = $WindowsAdkPaths.PathWinPEMedia

    $DestinationMedia = Join-Path $Destination 'media'
    if (-NOT (Test-Path $DestinationMedia)) {
        New-Item -Path $DestinationMedia -ItemType Directory -Force -ErrorAction Stop | Out-Null
    }

    $DestinationMount = Join-Path $Destination 'mount'
    if (-NOT (Test-Path $DestinationMount)) {
        New-Item -Path $DestinationMount -ItemType Directory -Force -ErrorAction Stop | Out-Null
    }

    $DestinationFirmwareFiles = Join-Path $Destination 'fwfiles'
    if (-NOT (Test-Path $DestinationFirmwareFiles)) {
        New-Item -Path $DestinationFirmwareFiles -ItemType Directory -Force -ErrorAction Stop | Out-Null
    }

    $DestinationSources = Join-Path $DestinationMedia 'sources'
    if (-NOT (Test-Path $DestinationSources)) {
        New-Item -Path $DestinationSources -ItemType Directory -Force -ErrorAction Stop | Out-Null
    }

    xcopy /herky "$AdkPathWinPEMedia" "$DestinationMedia\"

    Copy-Item "$AdkWimSourcePath" "$DestinationSources\boot.wim"
    Copy-Item "$AdkPathOscdimg\efisys.bin" "$DestinationFirmwareFiles"
    Copy-Item "$AdkPathOscdimg\etfsboot.com" "$DestinationFirmwareFiles"
    #=================================================
}
<#
.SYNOPSIS
Creates an ISO file from a bootable media directory using ADK tools
 
.DESCRIPTION
Creates an ISO file from a bootable media directory. Requires the Windows Assessment and Deployment Kit (ADK) to be installed.
 
.PARAMETER MediaPath
Path to the directory containing the bootable media
 
.PARAMETER isoFileName
Filename of the output ISO file
 
.PARAMETER isoLabel
Label of the ISO (limited to 16 characters)
 
.PARAMETER WindowsAdkRoot
Path to Windows ADK root directory. Optional if ADK is in default location.
 
.PARAMETER OpenExplorer
Switch to open Windows Explorer to the parent directory of the ISO file after creation
 
.EXAMPLE
New-AdkISO -MediaPath 'C:\BootMedia' -isoFileName 'WinPE.iso' -isoLabel 'WinPE'
Creates an ISO file from the bootable media
 
.NOTES
Author: David Segura - Recast Software
2026-07-10 - Updated help to follow OSD standard
2021-03-16 - Initial Release
 
.LINK
https://github.com/OSDeploy/OSD/tree/master/docs
#>

function New-AdkISO {
<#
.SYNOPSIS
Creates AdkISO resources.
 
.DESCRIPTION
Builds new AdkISO resources based on the provided parameters.
 
.PARAMETER WindowsAdkRoot
Specifies the WindowsAdkRoot to use when running New-AdkISO.
 
.PARAMETER MediaPath
Specifies the MediaPath to use when running New-AdkISO.
 
.PARAMETER isoFileName
Specifies the isoFileName to use when running New-AdkISO.
 
.PARAMETER isoLabel
Specifies the isoLabel to use when running New-AdkISO.
 
.PARAMETER NoPrompt
Specifies the NoPrompt to use when running New-AdkISO.
 
.PARAMETER Mount
Specifies the Mount to use when running New-AdkISO.
 
.PARAMETER OpenExplorer
Specifies the OpenExplorer to use when running New-AdkISO.
 
.EXAMPLE
New-AdkISO -WindowsAdkRoot <value>
Demonstrates a common way to run New-AdkISO.
 
.LINK
https://github.com/OSDeploy/OSD/tree/master/docs
 
.NOTES
Author: David Segura - Recast Software
2026-07-13 - Initial help block created
2026-07-13 - Refined generated help text
#>

    [CmdletBinding()]
    param (
        [Alias('AdkRoot')]
        [System.String]
        $WindowsAdkRoot,

        [Parameter(Mandatory = $true)]
        [string]$MediaPath,

        [Parameter(Mandatory = $true)]
        [string]$isoFileName,

        [Parameter(Mandatory = $true)]
        [ValidateLength(1,16)]
        [string]$isoLabel,

        #[System.Management.Automation.SwitchParameter]$NoPrompt,
        #[System.Management.Automation.SwitchParameter]$Mount,
        [System.Management.Automation.SwitchParameter]$OpenExplorer
    )
    Write-Warning "New-AdkISO has been replaced by New-WindowsAdkISO. Please update your scripts"
    Start-Sleep -Seconds 10
    #=================================================
    # Blocks
    #=================================================
    Block-WinPE
    if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Elevated Administrator rights are required"
    }
    Block-PowerShellVersionLt5
    #=================================================
    # Get Adk Paths
    #=================================================
    if ($WindowsAdkRoot) {
        $AdkPaths = Get-WindowsAdkPaths -WindowsAdkRoot $WindowsAdkRoot
    } else {
        $AdkPaths = Get-WindowsAdkPaths
    }
    if ($null -eq $AdkPaths) {
        Write-Warning "Could not get ADK going, sorry"
        Break
    }
    $WorkspacePath = (Get-Item -Path $MediaPath -ErrorAction Stop).Parent.FullName
    $IsoFullName = Join-Path $WorkspacePath $isoFileName
    $PathOscdimg = $AdkPaths.PathOscdimg
    $oscdimgexe = $AdkPaths.oscdimgexe

    Write-Verbose "WorkspacePath: $WorkspacePath"
    Write-Verbose "IsoFullName: $IsoFullName"
    Write-Verbose "PathOscdimg: $PathOscdimg"
    Write-Verbose "oscdimgexe: $oscdimgexe"
    #=================================================
    # Test Paths
    #=================================================
    $DestinationBoot = Join-Path $MediaPath 'boot'
    if (-NOT (Test-Path $DestinationBoot)) {
        Write-Warning "Cannot locate $DestinationBoot"
        Write-Warning "This does not appear to be a valid bootable ISO"
        Break
    }
    $DestinationEfiBoot = Join-Path $MediaPath 'efi\microsoft\boot'
    if (-NOT (Test-Path $DestinationEfiBoot)) {
        Write-Warning "Cannot locate $DestinationEfiBoot"
        Write-Warning "This does not appear to be a valid bootable ISO"
        Break
    }
    #=================================================
    # etfsboot.com
    #=================================================
    $etfsbootcom = $AdkPaths.etfsbootcom
    Copy-Item -Path $etfsbootcom -Destination $DestinationBoot -Force -ErrorAction Stop
    $Destinationetfsbootcom = Join-Path $DestinationBoot 'etfsboot.com'
    #=================================================
    # efisys.bin and efisys_noprompt.bin
    #=================================================
    $efisysbin = $AdkPaths.efisysbin
    Copy-Item -Path $efisysbin -Destination $DestinationEfiBoot -Force -ErrorAction Stop
    $Destinationefisysbin = Join-Path $DestinationEfiBoot 'efisys.bin'

    $efisysnopromptbin = $AdkPaths.efisysnopromptbin
    Copy-Item -Path $efisysnopromptbin -Destination $DestinationEfiBoot -Force -ErrorAction Stop
    $Destinationefisysnopromptbin = Join-Path $DestinationEfiBoot 'efisys_noprompt.bin'

<# if ($PSBoundParameters.ContainsKey('NoPrompt')) {
        $efisysbin = $AdkPaths.efisysnopromptbin
        Copy-Item -Path $efisysbin -Destination $DestinationEfiBoot -Force -ErrorAction Stop
        $Destinationefisysbin = Join-Path $DestinationEfiBoot 'efisys_noprompt.bin'
    } else {
        $efisysbin = $AdkPaths.efisysbin
        Copy-Item -Path $efisysbin -Destination $DestinationEfiBoot -Force -ErrorAction Stop
        $Destinationefisysbin = Join-Path $DestinationEfiBoot 'efisys.bin'
    } #>


    Write-Verbose  "DestinationBoot: $DestinationBoot"
    Write-Verbose  "etfsbootcom: $etfsbootcom"
    Write-Verbose  "Destinationetfsbootcom: $Destinationetfsbootcom"

    Write-Verbose  "DestinationEfiBoot: $DestinationEfiBoot"
    Write-Verbose  "efisysbin: $efisysbin"
    Write-Verbose  "Destinationefisysbin: $Destinationefisysbin"
    Write-Verbose  "efisysnopromptbin: $efisysnopromptbin"
    Write-Verbose  "Destinationefisysnopromptbin: $Destinationefisysnopromptbin"
    #=================================================
    # Strings
    #=================================================
    $isoLabelString = '-l"{0}"' -f "$isoLabel"
    Write-Verbose  "isoLabelString: $isoLabelString"
    #=================================================
    # Create Prompt ISO
    #=================================================
    $BootDataString = '2#p0,e,b"{0}"#pEF,e,b"{1}"' -f "$Destinationetfsbootcom", "$Destinationefisysbin"
    Write-Verbose  "BootDataString: $BootDataString"

    $Process = Start-Process $oscdimgexe -args @("-m","-o","-u2","-bootdata:$BootDataString",'-u2','-udfver102',$isoLabelString,"`"$MediaPath`"", "`"$IsoFullName`"") -PassThru -Wait -NoNewWindow

    if (-NOT (Test-Path $IsoFullName)) {
        Write-Error "Something didn't work"
        Break
    }
    $PromptIso = Get-Item -Path $IsoFullName
    #Write-Host -ForegroundColor Yellow "[$(Get-Date -format s)] ISO created at $PromptIso"
    #=================================================
    # Create NoPrompt ISO
    #=================================================
    $IsoFullName = "$($PromptIso.Directory)\$($PromptIso.BaseName)_NoPrompt.iso"
    $BootDataString = '2#p0,e,b"{0}"#pEF,e,b"{1}"' -f "$Destinationetfsbootcom", "$Destinationefisysnopromptbin"
    Write-Verbose  "BootDataString: $BootDataString"

    $Process = Start-Process $oscdimgexe -args @("-m","-o","-u2","-bootdata:$BootDataString",'-u2','-udfver102',$isoLabelString,"`"$MediaPath`"", "`"$IsoFullName`"") -PassThru -Wait -NoNewWindow

    if (-NOT (Test-Path $IsoFullName)) {
        Write-Error "Something didn't work"
        Break
    }
    $NoPromptIso = Get-Item -Path $IsoFullName
    #Write-Host -ForegroundColor Yellow "[$(Get-Date -format s)] ISO created at $NoPromptIso"
    #=================================================
    # OpenExplorer
    #=================================================
    if ($PSBoundParameters.ContainsKey('OpenExplorer')) {
        explorer $WorkspacePath
    }
    #=================================================
    # Return Get-Item
    #=================================================
    Return $PromptIso

<# $Results += [pscustomobject]@{
        FullName = $IsoFullName
        Name = $isoFileName
        Label = $isoLabel
        isoDirectory = $MediaPath
    }
    Return $Results #>

    #=================================================
}
function New-WindowsAdkISO {
    <#
    .SYNOPSIS
    Creates an ISO file from a bootable media directory using ADK
 
    .DESCRIPTION
    Creates an ISO file from a bootable media directory using Windows Assessment and Deployment Kit (ADK) tools.
 
    .PARAMETER MediaPath
    Path to the directory containing the bootable media
 
    .PARAMETER isoFileName
    Filename for the output ISO file
 
    .PARAMETER isoLabel
    Label for the ISO volume (limited to 16 characters)
 
    .PARAMETER WindowsAdkRoot
    Path to the Windows ADK root directory (optional if installed in default location)
 
    .EXAMPLE
    New-WindowsAdkISO -MediaPath 'C:\\Media' -isoFileName 'boot.iso' -isoLabel 'BootMedia'\n Creates an ISO file from the bootable media
 
    .NOTES
    Author: David Segura - Recast Software
    2026-07-10 - Updated help to follow OSD standard
    2025-03-01 - Updated to use Get-WindowsAdkPaths
    2025-02-26 - Initial Release replacing New-AdkISO
 
    .LINK
    https://github.com/OSDeploy/OSD/tree/master/docs
    #>

    [CmdletBinding()]
    param (
        # Directory containing the bootable media
        [Parameter(Mandatory = $true)]
        [ValidateScript({
                if (-NOT ($_ | Test-Path)) { throw "Path does not exist: $_" }
                if (-NOT ($_ | Test-Path -PathType Container)) { throw "Path must be a directory: $_" }
                return $true
            })]
        [System.IO.FileInfo]
        $MediaPath,

        # File Name of the ISO
        [Parameter(Mandatory = $true)]
        [System.String]
        $isoFileName,

        # Label of the ISO. Limited to 16 characters
        [Parameter(Mandatory = $true)]
        [ValidateLength(1,16)]
        [System.String]
        $isoLabel,

        [Parameter(Mandatory = $false)]
        [ValidateScript({
                if (-NOT ($_ | Test-Path)) { throw "Path does not exist: $_" }
                if (-NOT ($_ | Test-Path -PathType Container)) { throw "Path must be a directory: $_" }
                return $true
            })]
        [System.IO.FileInfo]
        $IsoDirectory = $((Get-Item -Path $MediaPath -ErrorAction Stop).Parent.FullName),

        # Path to the Windows ADK root directory. Typically 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit'
        [ValidateScript({
                if (-NOT ($_ | Test-Path)) { throw "Path does not exist: $_" }
                if (-NOT ($_ | Test-Path -PathType Container)) { throw "Path must be a directory: $_" }
                if (-NOT (Test-Path "$($_.FullName)\Deployment Tools")) { throw "Path does not contain a Deployment Tools subfolder: $_" }
                # if (-NOT (Test-Path "$($_.FullName)\Windows Preinstallation Environment")) { throw "Path does not contain a Windows Preinstallation Environment directory: $_"}
                return $true
            })]
        [System.IO.FileInfo]
        $WindowsAdkRoot,

        #[System.Management.Automation.SwitchParameter]$NoPrompt,
        #[System.Management.Automation.SwitchParameter]$Mount,

        # Opens Windows Explorer to the parent directory of the ISO File
        [System.Management.Automation.SwitchParameter]
        $OpenExplorer
    )
    #=================================================
    $Error.Clear()
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start"
    #=================================================
    Block-WinPE
        if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Elevated Administrator rights are required"
        }
    Block-PowerShellVersionLt5
    #=================================================
    # Get Adk Paths
    if ($WindowsAdkRoot) {
        $WindowsAdkPaths = Get-WindowsAdkPaths -WindowsAdkRoot $WindowsAdkRoot
    } else {
        $WindowsAdkPaths = Get-WindowsAdkPaths
    }
    if ($null -eq $WindowsAdkPaths) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Could not get ADK going, sorry"
        Break
    }
    $IsoFullName = Join-Path $IsoDirectory $isoFileName
    $PathOscdimg = $WindowsAdkPaths.PathOscdimg
    $oscdimgexe = $WindowsAdkPaths.oscdimgexe

    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] IsoDirectory: $IsoDirectory"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] IsoFullName: $IsoFullName"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] PathOscdimg: $PathOscdimg"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] oscdimgexe: $oscdimgexe"
    #=================================================
    # Test Paths
    $DestinationBoot = Join-Path $MediaPath 'boot'
    if (-NOT (Test-Path $DestinationBoot)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Cannot locate $DestinationBoot"
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] This does not appear to be a valid bootable ISO"
        Break
    }
    $DestinationEfiBoot = Join-Path $MediaPath 'efi\microsoft\boot'
    if (-NOT (Test-Path $DestinationEfiBoot)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Cannot locate $DestinationEfiBoot"
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] This does not appear to be a valid bootable ISO"
        Break
    }
    #=================================================
    # etfsboot.com
    $etfsbootcom = $WindowsAdkPaths.etfsbootcom
    $Destinationetfsbootcom = Join-Path $DestinationBoot 'etfsboot.com'
    if (Test-Path $Destinationetfsbootcom) {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Using existing $Destinationetfsbootcom"
    }
    else {
        Copy-Item -Path $etfsbootcom -Destination $DestinationBoot -Force -ErrorAction Stop
    }
    #=================================================
    # efisys.bin and efisys_noprompt.bin
    $efisysbin = $WindowsAdkPaths.efisysbin
    $Destinationefisysbin = Join-Path $DestinationEfiBoot 'efisys.bin'
    if (Test-Path $Destinationefisysbin) {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Using existing $Destinationefisysbin"
    }
    else {
        Copy-Item -Path $efisysbin -Destination $DestinationEfiBoot -Force -ErrorAction Stop
    }

    $efisysnopromptbin = $WindowsAdkPaths.efisysnopromptbin
    $Destinationefisysnopromptbin = Join-Path $DestinationEfiBoot 'efisys_noprompt.bin'
    if (Test-Path $Destinationefisysnopromptbin) {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Using existing $Destinationefisysnopromptbin"
    }
    else {
        Copy-Item -Path $efisysnopromptbin -Destination $DestinationEfiBoot -Force -ErrorAction Stop
    }

<# if ($PSBoundParameters.ContainsKey('NoPrompt')) {
        $efisysbin = $WindowsAdkPaths.efisysnopromptbin
        Copy-Item -Path $efisysbin -Destination $DestinationEfiBoot -Force -ErrorAction Stop
        $Destinationefisysbin = Join-Path $DestinationEfiBoot 'efisys_noprompt.bin'
    } else {
        $efisysbin = $WindowsAdkPaths.efisysbin
        Copy-Item -Path $efisysbin -Destination $DestinationEfiBoot -Force -ErrorAction Stop
        $Destinationefisysbin = Join-Path $DestinationEfiBoot 'efisys.bin'
    } #>


    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] DestinationBoot: $DestinationBoot"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] etfsbootcom: $etfsbootcom"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Destinationetfsbootcom: $Destinationetfsbootcom"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] DestinationEfiBoot: $DestinationEfiBoot"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] efisysbin: $efisysbin"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Destinationefisysbin: $Destinationefisysbin"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] efisysnopromptbin: $efisysnopromptbin"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Destinationefisysnopromptbin: $Destinationefisysnopromptbin"
    #=================================================
    # Strings
    $isoLabelString = '-l"{0}"' -f "$isoLabel"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] isoLabelString: $isoLabelString"
    #=================================================
    # Create Prompt ISO
    $BootDataString = '2#p0,e,b"{0}"#pEF,e,b"{1}"' -f "$Destinationetfsbootcom", "$Destinationefisysbin"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] BootDataString: $BootDataString"

    $Process = Start-Process $oscdimgexe -args @('-m', '-o', '-u2', "-bootdata:$BootDataString", '-u2', '-udfver102', $isoLabelString, "`"$MediaPath`"", "`"$IsoFullName`"") -PassThru -Wait -WindowStyle Hidden

    if (-NOT (Test-Path $IsoFullName)) {
        Write-Error "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Something didn't work"
        Break
    }
    $PromptIso = Get-Item -Path $IsoFullName
    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] ISO created at $PromptIso"
    #=================================================
    # Create NoPrompt ISO
    $IsoFullName = "$($PromptIso.Directory)\$($PromptIso.BaseName)_NoPrompt.iso"
    $BootDataString = '2#p0,e,b"{0}"#pEF,e,b"{1}"' -f "$Destinationetfsbootcom", "$Destinationefisysnopromptbin"
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] BootDataString: $BootDataString"

    $Process = Start-Process $oscdimgexe -args @('-m', '-o', '-u2', "-bootdata:$BootDataString", '-u2', '-udfver102', $isoLabelString, "`"$MediaPath`"", "`"$IsoFullName`"") -PassThru -Wait -WindowStyle Hidden

    if (-NOT (Test-Path $IsoFullName)) {
        Write-Error "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] Something didn't work"
        Break
    }
    $NoPromptIso = Get-Item -Path $IsoFullName
    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand)] ISO created at $NoPromptIso"
    #=================================================
    # Open Windows Explorer
    if ($PSBoundParameters.ContainsKey('OpenExplorer')) {
        explorer $IsoDirectory
    }
    #=================================================
    # Return Get-Item
    Return $PromptIso

    <# $Results += [pscustomobject]@{
        FullName = $IsoFullName
        Name = $isoFileName
        Label = $isoLabel
        isoDirectory = $MediaPath
    }
    Return $Results #>

    #=================================================
}