public/Install-OSDeployMDT.ps1

function Install-OSDeployMDT {
    <#
    .SYNOPSIS
        Audits or initializes an MDT deployment share for OSDeployMDT
 
    .DESCRIPTION
        Resolves the MDT installation directory, interactively selects a deployment share, and
        audits the OSDeployMDT template and Settings.xml configuration. Without Force, missing
        or differing items are reported as skipped and no files are written.
 
        With Force, the function creates Templates, Templates\winpe-drivers, and
        Templates\winpe-extrafiles; overwrites winpeshl.ini, wimscript.ini,
        Unattend_PE_x64.xml, LiteTouchPE.xml, and background.bmp from the MDT installation;
        changes the Unattend and winpeshl copy sources in LiteTouchPE.xml from INSTALLDIR to
        DEPLOYROOT; and adds the Invoke-OSDeployMDT exit command.
 
        When Boot\LiteTouchPE_x64.xml exists, its /Definition/WindowsPE/Components node is
        imported into Templates\LiteTouchPE.xml. When it does not exist, the template receives
        the function's default component list. Control\Settings.xml is backed up once as
        Settings.xml.backup, then exact matching values are changed to use the copied
        background, 512 MB scratch space, the empty x64 extra-directory value is set to
        Templates\winpe-extrafiles, and enabled x86 settings are changed to False.
 
        WhatIf or a declined Confirm stops processing before either audit reporting or changes.
 
    .PARAMETER Force
        Writes the audited deployment-share changes. When omitted, the command only reports
        existing, missing, and differing items. This switch does not bypass WhatIf or Confirm.
 
    .EXAMPLE
        PS> Install-OSDeployMDT
 
        Prompts for a deployment share and audits its OSDeployMDT configuration.
 
    .EXAMPLE
        PS> Install-OSDeployMDT -Force
 
        Prompts for a deployment share and writes the required template and Settings.xml changes.
 
    .EXAMPLE
        PS> Install-OSDeployMDT -WhatIf
 
        Selects a deployment share and displays the top-level initialization operation without
        running the audit or changing files.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        None.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 1.0.0
        Date: 2026-08-28
 
        Requires Windows 11 25H2 or later, PowerShell 7.4 or later installed from MSI,
        curl.exe, MDT, and Administrator rights.
 
        Source files that are absent from the MDT installation produce warnings. XML updates
        are applied only when the expected nodes or exact source values are present.
 
    .LINK
        Invoke-OSDeployMDT
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([void])]
    param (
        [Parameter()]
        [switch] $Force
    )
    #=================================================
    # Stop before inspecting or modifying MDT when a required host capability is missing.
    if (-not (Test-IsWindows11)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Windows 11 is required."
    }
    if (-not (Test-IsWindows1125H2)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Windows 11 25H2 (build 26200) is required."
    }
    if (-not (Test-PwshVersionMin)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] PowerShell 7.4 or higher is required."
    }
    if (-not (Test-PwshPSHome)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] The MSI installation of PowerShell 7 is required."
    }
    if (-not (Test-CommandCurl)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] curl.exe is required but was not found in the current PATH. curl.exe ships with Windows 10 1803+."
    }
    if (-not (Test-IsAdministrator)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Administrator rights are required. Re-run PowerShell as Administrator and try again."
    }
    #=================================================
    # Stop when MDT installation paths cannot be resolved.
    #region Resolve MDT installation directory
    $installDir = Get-OSDeployMDTInstallDir
    if ($null -eq $installDir) {
        Write-Warning "[$(Get-Date -Format s)] MDT installation directory (INSTALLDIR) could not be resolved. Aborting."
        return
    }
    Write-Host "[$(Get-Date -Format s)] INSTALLDIR '$installDir'" -ForegroundColor DarkGray
    #endregion

    # Stop when the user cancels deployment-share selection.
    #region Select the active deployment share
    $share = Select-OSDeployMDTDeploymentShare
    if ($null -eq $share) {
        Write-Warning "[$(Get-Date -Format s)] No deployment share selected. Aborting."
        return
    }
    #endregion

    $deployRoot = $share.Path

    # Honor WhatIf and Confirm before changing the selected deployment share.
    if (-not $PSCmdlet.ShouldProcess($deployRoot, 'Initialize MDT Deployment Share')) { return }

    Write-Host "[$(Get-Date -Format s)] Initializing MDT DeployRoot $deployRoot" -ForegroundColor DarkCyan

    #region Ensure Templates folder structure exists
    $templateFolders = @(
        (Join-Path -Path $deployRoot -ChildPath 'Templates')
        (Join-Path -Path $deployRoot -ChildPath 'Templates\winpe-drivers')
        (Join-Path -Path $deployRoot -ChildPath 'Templates\winpe-extrafiles')
    )
    foreach ($folder in $templateFolders) {
        if (-not (Test-Path -LiteralPath $folder)) {
            if ($Force) {
                New-Item -Path $folder -ItemType Directory -Force | Out-Null
                Write-Host "[$(Get-Date -Format s)] [NEW] $folder" -ForegroundColor DarkGreen
            }
            else {
                Write-Host "[$(Get-Date -Format s)] [SKIP] $folder (use -Force to create)" -ForegroundColor DarkYellow
            }
        }
        else {
            Write-Host "[$(Get-Date -Format s)] [OK] $folder" -ForegroundColor DarkGray
        }
    }
    $templatesFolder = $templateFolders[0]
    #endregion


    #region Copy template files from INSTALLDIR\Templates to DEPLOYROOT\Templates
    Write-Host "[$(Get-Date -Format s)] Copy files from $installDir\Templates" -ForegroundColor DarkCyan
    $templateCopyMap = @(
        [ordered]@{ SourceName = 'winpeshl.ini';         DestinationName = 'winpeshl.ini' }
        [ordered]@{ SourceName = 'Wimscript.ini';        DestinationName = 'wimscript.ini' }
        [ordered]@{ SourceName = 'Unattend_PE_x64.xml';  DestinationName = 'Unattend_PE_x64.xml' }
        [ordered]@{ SourceName = 'LiteTouchPE.xml';      DestinationName = 'LiteTouchPE.xml' }
    )
    foreach ($copyItem in $templateCopyMap) {
        $sourcePath = Join-Path -Path $installDir -ChildPath (Join-Path -Path 'Templates' -ChildPath $copyItem.SourceName)
        $destPath = Join-Path -Path $templatesFolder -ChildPath $copyItem.DestinationName

        if (Test-Path -LiteralPath $sourcePath) {
            if ($Force) {
                Copy-Item -LiteralPath $sourcePath -Destination $destPath -Force
                Write-Host "[$(Get-Date -Format s)] [COPY] $destPath" -ForegroundColor DarkGreen
            }
            elseif (-not (Test-Path -LiteralPath $destPath)) {
                Write-Host "[$(Get-Date -Format s)] [SKIP] $destPath (use -Force to copy)" -ForegroundColor DarkYellow
            }
            else {
                Write-Host "[$(Get-Date -Format s)] [OK] $destPath" -ForegroundColor DarkGray
            }
        }
        else {
            Write-Warning "[$(Get-Date -Format s)] Source file not found: '$sourcePath'"
        }
    }
    $destXml = Join-Path -Path $templatesFolder -ChildPath 'LiteTouchPE.xml'
    #endregion

    #region Copy Background.bmp from INSTALLDIR\Samples to DEPLOYROOT\Templates (no overwrite)
    Write-Host "[$(Get-Date -Format s)] Copy files from $installDir\Samples" -ForegroundColor DarkCyan
    $sourceBackground = Join-Path -Path $installDir -ChildPath 'Samples\Background.bmp'
    $destBackground   = Join-Path -Path $templatesFolder -ChildPath 'background.bmp'

    if (Test-Path -Path $sourceBackground) {
        if ($Force) {
            Copy-Item -Path $sourceBackground -Destination $destBackground -Force
            Write-Host "[$(Get-Date -Format s)] [COPY] $destBackground" -ForegroundColor DarkGreen
        }
        elseif (-not (Test-Path -Path $destBackground)) {
            Write-Host "[$(Get-Date -Format s)] [SKIP] $destBackground (use -Force to copy)" -ForegroundColor DarkYellow
        }
        else {
            Write-Host "[$(Get-Date -Format s)] [OK] $destBackground" -ForegroundColor DarkGray
        }
    }
    else {
        Write-Warning "[$(Get-Date -Format s)] Source file not found: '$sourceBackground'"
    }
    #endregion

    Write-Host "[$(Get-Date -Format s)] Update $destXml" -ForegroundColor DarkCyan

    #region Rewrite INSTALLDIR -> DEPLOYROOT for Unattend and winpeshl Copy sources in LiteTouchPE.xml
    # Write-Host "[$(Get-Date -Format s)] Update INSTALLDIR to DEPLOYROOT in $destXml" -ForegroundColor DarkCyan
    if (Test-Path -Path $destXml) {
        try {
            $xmlContent = Get-Content -Path $destXml -Raw
            $changed = $false

            $replacements = @(
                @{ Find = '%INSTALLDIR%\Templates\Unattend_PE_%PLATFORM%.xml'; Replace = '%DEPLOYROOT%\Templates\Unattend_PE_%PLATFORM%.xml' }
                @{ Find = '%INSTALLDIR%\Templates\winpeshl.ini';              Replace = '%DEPLOYROOT%\Templates\winpeshl.ini' }
            )

            foreach ($r in $replacements) {
                if ($xmlContent -match [regex]::Escape($r.Find)) {
                    if ($Force) {
                        $xmlContent = $xmlContent -replace [regex]::Escape($r.Find), $r.Replace
                        Write-Host "[$(Get-Date -Format s)] [UPDATE] $($r.Replace)" -ForegroundColor DarkGreen
                        $changed = $true
                    }
                    else {
                        Write-Host "[$(Get-Date -Format s)] [SKIP] $($r.Replace) (use -Force to update)" -ForegroundColor DarkYellow
                    }
                }
                else {
                    Write-Host "[$(Get-Date -Format s)] [OK] $($r.Replace)" -ForegroundColor DarkGray
                }
            }

            if ($changed) {
                Set-Content -Path $destXml -Value $xmlContent -Encoding UTF8 -NoNewline
            }
        }
        catch {
            Write-Warning "[$(Get-Date -Format s)] Failed to update Copy sources in '$destXml': $_"
        }
    }
    #endregion

    #region Mirror Components from LiteTouchPE_x64.xml into Templates\LiteTouchPE.xml
    # Write-Host "[$(Get-Date -Format s)] Update WindowsPE Components in $destXml" -ForegroundColor DarkCyan
    $bootXmlPath = Join-Path -Path $deployRoot -ChildPath 'Boot\LiteTouchPE_x64.xml'

    if ((Test-Path -Path $bootXmlPath) -and (Test-Path -Path $destXml)) {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Merging Components from $bootXmlPath"

        try {
            $bootXml = New-Object -TypeName System.Xml.XmlDocument
            $bootXml.Load($bootXmlPath)

            $templateXml = New-Object -TypeName System.Xml.XmlDocument
            $templateXml.Load($destXml)

            $bootComponents = $bootXml.SelectSingleNode('/Definition/WindowsPE/Components')

            if ($null -ne $bootComponents) {
                $templateComponents = $templateXml.SelectSingleNode('/Definition/WindowsPE/Components')

                # Import the node from the boot XML document into the template document
                $importedNode = $templateXml.ImportNode($bootComponents, $true)

                if ($null -ne $templateComponents) {
                    # Replace existing Components node
                    $templateComponents.ParentNode.ReplaceChild($importedNode, $templateComponents) | Out-Null
                }
                else {
                    # Append Components under WindowsPE if not present
                    $windowsPE = $templateXml.SelectSingleNode('/Definition/WindowsPE')
                    if ($null -ne $windowsPE) {
                        $windowsPE.AppendChild($importedNode) | Out-Null
                    }
                }

                if ($Force) {
                    $templateXml.Save($destXml)
                    Write-Host "[$(Get-Date -Format s)] [MERGE] Existing WindowsPE Components from Boot\LiteTouchPE_x64.xml" -ForegroundColor DarkGreen
                }
                else {
                    Write-Host "[$(Get-Date -Format s)] [SKIP] WindowsPE Components from Boot\LiteTouchPE_x64.xml (use -Force to merge)" -ForegroundColor DarkYellow
                }
            }
            else {
                Write-Host "[$(Get-Date -Format s)] [OK] No Components element found in Boot\LiteTouchPE_x64.xml" -ForegroundColor DarkGray
            }
        }
        catch {
            Write-Warning "[$(Get-Date -Format s)] Failed to merge Components from LiteTouchPE_x64.xml: $_"
        }
    }
    elseif (-not (Test-Path -Path $bootXmlPath)) {
        # Write-Host "[$(Get-Date -Format s)] '$bootXmlPath' not found - writing default Components" -ForegroundColor DarkGray

        if (Test-Path -Path $destXml) {
            try {
                $defaultComponents = @(
                    'winpe-scripting'
                    'winpe-hta'
                    'winpe-wmi'
                    'winpe-securestartup'
                    'winpe-fmapi'
                    'winpe-dismcmdlets'
                    'winpe-dot3svc'
                    'winpe-enhancedstorage'
                    'winpe-hsp-driver'
                    'winpe-netfx'
                    'winpe-platformid'
                    'winpe-pmemcmdlets'
                    'winpe-powershell'
                    'winpe-pppoe'
                    'winpe-rndis'
                    'winpe-securebootcmdlets'
                    'winpe-storagewmi'
                    'winpe-wds-tools'
                )

                $templateXml = New-Object -TypeName System.Xml.XmlDocument
                $templateXml.Load($destXml)

                # Locate or create the Components node under /Definition/WindowsPE
                $templateComponents = $templateXml.SelectSingleNode('/Definition/WindowsPE/Components')

                if ($null -eq $templateComponents) {
                    $windowsPE = $templateXml.SelectSingleNode('/Definition/WindowsPE')
                    if ($null -ne $windowsPE) {
                        $templateComponents = $templateXml.CreateElement('Components')
                        $windowsPE.AppendChild($templateComponents) | Out-Null
                    }
                }

                if ($null -ne $templateComponents) {
                    # Remove any existing Component children before writing defaults
                    $templateComponents.RemoveAll()

                    foreach ($name in $defaultComponents) {
                        $componentNode = $templateXml.CreateElement('Component')
                        $componentNode.InnerText = $name
                        $templateComponents.AppendChild($componentNode) | Out-Null
                    }

                    if ($Force) {
                        $templateXml.Save($destXml)
                        Write-Host "[$(Get-Date -Format s)] [UPDATE] WindowsPE Components updated in $destXml" -ForegroundColor DarkGreen
                    }
                    else {
                        Write-Host "[$(Get-Date -Format s)] [SKIP] WindowsPE default Components (use -Force to update)" -ForegroundColor DarkYellow
                    }
                }
            }
            catch {
                Write-Warning "[$(Get-Date -Format s)] Failed to update default Components to template: $_"
            }
        }
    }
    #endregion

    #region Ensure Invoke-OSDeployMDT Exit entry exists in LiteTouchPE.xml
    # Write-Host "[$(Get-Date -Format s)] Update Exits in $destXml" -ForegroundColor DarkCyan
    $buildMdtExit = 'start /wait pwsh.exe -ExecutionPolicy Bypass -Command "Invoke-OSDeployMDT"'

    if (Test-Path -Path $destXml) {
        try {
            $exitXml = New-Object -TypeName System.Xml.XmlDocument
            $exitXml.Load($destXml)

            $exitsNode = $exitXml.SelectSingleNode('/Definition/WindowsPE/Exits')

            if ($null -eq $exitsNode) {
                $windowsPE = $exitXml.SelectSingleNode('/Definition/WindowsPE')
                if ($null -ne $windowsPE) {
                    $exitsNode = $exitXml.CreateElement('Exits')
                    $windowsPE.AppendChild($exitsNode) | Out-Null
                }
            }

            if ($null -ne $exitsNode) {
                # Check if the entry already exists
                $existing = $exitsNode.SelectSingleNode("Exit[. = '$buildMdtExit']")

                if ($null -eq $existing) {
                    if ($Force) {
                        $exitNode = $exitXml.CreateElement('Exit')
                        $exitNode.InnerText = $buildMdtExit
                        $exitsNode.AppendChild($exitNode) | Out-Null
                        $exitXml.Save($destXml)
                        Write-Host "[$(Get-Date -Format s)] [ADD] start /wait pwsh.exe -ExecutionPolicy Bypass -Command `"Invoke-OSDeployMDT`"" -ForegroundColor DarkGreen
                    }
                    else {
                        Write-Host "[$(Get-Date -Format s)] [SKIP] Invoke-OSDeployMDT Exit entry (use -Force to add)" -ForegroundColor DarkYellow
                    }
                }
                else {
                    Write-Host "[$(Get-Date -Format s)] [OK] start /wait pwsh.exe -ExecutionPolicy Bypass -Command `"Invoke-OSDeployMDT`"" -ForegroundColor DarkGray
                }
            }
        }
        catch {
            Write-Warning "[$(Get-Date -Format s)] Failed to update Exits in '$destXml': $_"
        }
    }
    #endregion


    #region Update DEPLOYROOT\Control\Settings.xml
    $settingsXmlPath = Join-Path -Path $deployRoot -ChildPath 'Control\Settings.xml'

    if (Test-Path -Path $settingsXmlPath) {

        #region Backup Settings.xml (no overwrite)
        $settingsBackupPath = Join-Path -Path $deployRoot -ChildPath 'Control\Settings.xml.backup'
        if (-not (Test-Path -Path $settingsBackupPath)) {
            if ($Force) {
                Copy-Item -Path $settingsXmlPath -Destination $settingsBackupPath
                Write-Host "[$(Get-Date -Format s)] [BACKUP] $settingsBackupPath" -ForegroundColor DarkGreen
            }
            else {
                Write-Host "[$(Get-Date -Format s)] [SKIP] $settingsBackupPath (use -Force to create)" -ForegroundColor DarkYellow
            }
        }
        else {
            Write-Host "[$(Get-Date -Format s)] [OK] $settingsBackupPath" -ForegroundColor DarkGray
        }
        #endregion

        Write-Host "[$(Get-Date -Format s)] Update x64 Settings in $settingsXmlPath" -ForegroundColor DarkCyan
        try {
            $settingsContent = Get-Content -Path $settingsXmlPath -Raw
            $changed = $false

            $replacements = @(
                @{ Find = '%INSTALLDIR%\Samples\Background.bmp';                                          Replace = '%DEPLOYROOT%\Templates\background.bmp';                               IsRegex = $false }
                @{ Find = '(?s)<Boot\.x64\.ExtraDirectory>\s*</Boot\.x64\.ExtraDirectory>';               Replace = '<Boot.x64.ExtraDirectory>%DEPLOYROOT%\Templates\winpe-extrafiles</Boot.x64.ExtraDirectory>'; IsRegex = $true; ExistsPattern = '<Boot\.x64\.ExtraDirectory>' }
                @{ Find = '<Boot.x64.ScratchSpace>32</Boot.x64.ScratchSpace>';                            Replace = '<Boot.x64.ScratchSpace>512</Boot.x64.ScratchSpace>';                   IsRegex = $false }
            )

            foreach ($r in $replacements) {
                $pattern = if ($r.IsRegex) { $r.Find } else { [regex]::Escape($r.Find) }
                if ($settingsContent -match $pattern) {
                    if ($Force) {
                        $settingsContent = $settingsContent -replace $pattern, $r.Replace
                        Write-Host "[$(Get-Date -Format s)] [UPDATE] $($r.Replace)" -ForegroundColor DarkGreen
                        $changed = $true
                    }
                    else {
                        Write-Host "[$(Get-Date -Format s)] [SKIP] $($r.Replace) (use -Force to update)" -ForegroundColor DarkYellow
                    }
                }
                else {
                    if ($r.ExistsPattern -and ($settingsContent -match $r.ExistsPattern)) {
                        Write-Host "[$(Get-Date -Format s)] Not modifying '$($r.Find)' - already has a value" -ForegroundColor DarkGray
                    }
                    else {
                        Write-Host "[$(Get-Date -Format s)] [OK] $($r.Replace)" -ForegroundColor DarkGray
                    }
                }
            }

            if ($changed) {
                Set-Content -Path $settingsXmlPath -Value $settingsContent -Encoding UTF8 -NoNewline
            }
        }
        catch {
            Write-Warning "[$(Get-Date -Format s)] Failed to update '$settingsXmlPath': $_"
        }

        Write-Host "[$(Get-Date -Format s)] Disable x86 Settings in $settingsXmlPath" -ForegroundColor DarkCyan
        try {
            $settingsContent = Get-Content -Path $settingsXmlPath -Raw
            $changed = $false

            $replacements = @(
                @{ Find = '<SupportX86>True</SupportX86>';                                                Replace = '<SupportX86>False</SupportX86>';                                       IsRegex = $false }
                @{ Find = '<Boot.x86.UseBootWim>True</Boot.x86.UseBootWim>';                              Replace = '<Boot.x86.UseBootWim>False</Boot.x86.UseBootWim>';                     IsRegex = $false }
                @{ Find = '<Boot.x86.IncludeAllDrivers>True</Boot.x86.IncludeAllDrivers>';                Replace = '<Boot.x86.IncludeAllDrivers>False</Boot.x86.IncludeAllDrivers>';       IsRegex = $false }
                @{ Find = '<Boot.x86.IncludeNetworkDrivers>True</Boot.x86.IncludeNetworkDrivers>';        Replace = '<Boot.x86.IncludeNetworkDrivers>False</Boot.x86.IncludeNetworkDrivers>';IsRegex = $false }
                @{ Find = '<Boot.x86.IncludeMassStorageDrivers>True</Boot.x86.IncludeMassStorageDrivers>';Replace = '<Boot.x86.IncludeMassStorageDrivers>False</Boot.x86.IncludeMassStorageDrivers>';IsRegex = $false }
                @{ Find = '<Boot.x86.IncludeVideoDrivers>True</Boot.x86.IncludeVideoDrivers>';            Replace = '<Boot.x86.IncludeVideoDrivers>False</Boot.x86.IncludeVideoDrivers>';   IsRegex = $false }
                @{ Find = '<Boot.x86.IncludeSystemDrivers>True</Boot.x86.IncludeSystemDrivers>';          Replace = '<Boot.x86.IncludeSystemDrivers>False</Boot.x86.IncludeSystemDrivers>'; IsRegex = $false }
                @{ Find = '<Boot.x86.GenerateLiteTouchISO>True</Boot.x86.GenerateLiteTouchISO>';          Replace = '<Boot.x86.GenerateLiteTouchISO>False</Boot.x86.GenerateLiteTouchISO>'; IsRegex = $false }
            )

            foreach ($r in $replacements) {
                $pattern = if ($r.IsRegex) { $r.Find } else { [regex]::Escape($r.Find) }
                if ($settingsContent -match $pattern) {
                    if ($Force) {
                        $settingsContent = $settingsContent -replace $pattern, $r.Replace
                        Write-Host "[$(Get-Date -Format s)] [DISABLE] $($r.Replace)" -ForegroundColor DarkGray
                        $changed = $true
                    }
                    else {
                        Write-Host "[$(Get-Date -Format s)] [SKIP] $($r.Replace) (use -Force to disable)" -ForegroundColor DarkGray
                    }
                }
                else {
                    if ($r.ExistsPattern -and ($settingsContent -match $r.ExistsPattern)) {
                        Write-Host "[$(Get-Date -Format s)] Not modifying '$($r.Find)' - already has a value" -ForegroundColor DarkGray
                    }
                    else {
                        Write-Host "[$(Get-Date -Format s)] [OK] $($r.Replace)" -ForegroundColor DarkGray
                    }
                }
            }

            if ($changed) {
                Set-Content -Path $settingsXmlPath -Value $settingsContent -Encoding UTF8 -NoNewline
            }
        }
        catch {
            Write-Warning "[$(Get-Date -Format s)] Failed to update '$settingsXmlPath': $_"
        }
    }
    else {
        Write-Host "[$(Get-Date -Format s)] Settings.xml not found" -ForegroundColor DarkGray
    }
    #endregion


    if ($Force) {
        Write-Host
        Write-Host "🚗 WinPE Drivers can be added to $templatesFolder\winpe-drivers" -ForegroundColor DarkCyan
        Write-Host "➕ WinPE ExtraFiles can be added to $templatesFolder\winpe-extrafiles" -ForegroundColor DarkCyan
        Write-Host "🧱 Wallpaper can be customized by replacing $destBackground" -ForegroundColor DarkCyan
        Write-Host "⚙️ Invoke-OSDeployMDT will run when running Update Deployment Share" -ForegroundColor DarkCyan
        Write-Host "🎉 PowerShell Gallery will now work in your bootimage" -ForegroundColor DarkCyan
        Write-Host "☁️ OSDCloud will now work in your bootimage by running Deploy-OSDCloud in PowerShell" -ForegroundColor DarkCyan
    }
}