Public/Functions/OSD.WinPE.ps1

if ($env:SystemDrive -eq 'X:') {
    <#
    .SYNOPSIS
    Common WinPE Commands using wpeutil and Microsoft DaRT RemoteRecovery
 
    .DESCRIPTION
    Common WinPE Commands using wpeutil and Microsoft DaRT RemoteRecovery
 
    .LINK
    https://github.com/OSDeploy/OSD/tree/master/Docs
 
    .NOTES
    19.10.1 David Segura @SeguraOSD
    #>

    function Get-OSDWinPE {
        [CmdletBinding()]
        param (
            #Find and Copy PowerShell Modules to WinPE
            #Searches all PSDrives for <drive>:\Modules directory
            #Searches all PSDrives for <drive>:\Content\Modules directory
            #Copies Modules to X:\Program Files\WindowsPowerShell\Modules
            [Alias('Modules','AddModules')]
            [System.Management.Automation.SwitchParameter]$GetModules,

            #Find and Run PowerShell Scripts
            #Searches all PSDrives for <drive>:\<$GetScript>
            #Searches all PSDrives for <drive>:\Content\Scripts\<$GetScript>
            #Calls found scripts in the current PS Session
            [Alias('Script','CallScript')]
            [string[]]$GetScripts,

            #wpeutil InitializeNetwork
            #Initializes network components and drivers and sets the computer name to a randomly-chosen value
            [Alias('Network')]
            [System.Management.Automation.SwitchParameter]$InitializeNetwork,

            #wpeutil InitializeNetwork /NoWait
            #Initializes network components and drivers and sets the computer name to a randomly-chosen value
            #The /NoWait option will skip the time where your PC would otherwise wait to acquire an IP address
            #If you don't use /NoWait, Windows PE will wait to acquire an address before it finishes loading your WinPE session
            #/NoWait is helpful for environments that don't use DHCP
            [Alias('NetworkNoWait')]
            [System.Management.Automation.SwitchParameter]$InitializeNetworkNoWait,

            #wpeutil WaitForNetwork
            #Waits for the network card to be initialized
            #Use this command when creating scripts to make sure that the network card has been fully initialized before continuing
            [Alias('WaitNetwork')]
            [System.Management.Automation.SwitchParameter]$WaitForNetwork,

            #wpeutil WaitForRemovableStorage
            #During the Windows PE startup sequence, this command will block startup until the removable storage devices, such as USB hard drives, are initialized
            [Alias('WaitUSB')]
            [System.Management.Automation.SwitchParameter]$WaitForRemovableStorage,

            #wpeutil DisableFirewall
            #Disables the Firewall
            [Alias('Disable')]
            [System.Management.Automation.SwitchParameter]$DisableFirewall,

            #wpeutil UpdateBootInfo
            #Populates the registry with information about how Windows PE boots
            #After you run this command, query the registry. For example:
            #reg query HKLM\System\CurrentControlSet\Control /v PEBootType
            #The results of this operation might change after loading additional driver support.
            #To determine where Windows PE is booted from, examine the following:
            # PEBootType: Error, Flat, Remote, Ramdisk:SourceIdentified Ramdisk:SourceUnidentified, Ramdisk:OpticalDrive
            # PEBootTypeErrorCode: HRESULT code
            # PEBootServerName: Windows Deployment Services server name
            # PEBootServerAddr: Windows Deployment Services server IP address
            # PEBootRamdiskSourceDrive: Source drive letter, if available.
            # PEFirmwareType: Firmware boot mode: 0x1 for BIOS, 0x2 for UEFI.
            #If you are not booting Windows Deployment Services, the best way to determine where Windows PE booted from is to first check for PEBootRamdiskSourceDrive registry key
            #If it is not present, scan the drives of the correct PEBootType and look for some kind of tag file that identifies the boot drive
            [Alias('Update')]
            [System.Management.Automation.SwitchParameter]$UpdateBootInfo,

            #RemoteRecovery.exe -nomessage
            #Microsoft Diagnostic and Recovery Toolset Remote Recovery
            [Alias('Remote')]
            [System.Management.Automation.SwitchParameter]$RemoteRecovery,

            #wpeutil Reboot
            #Reboots the computer
            [System.Management.Automation.SwitchParameter]$Reboot,
            
            #wpeutil Shutdown
            #Shutdown the computer
            [System.Management.Automation.SwitchParameter]$Shutdown
        )
        #=================================================
        # Blocks
        #=================================================
        Block-WinOS
        #=================================================
        # Increase the Console Screen Buffer size
        #=================================================
        if (!(Test-Path "HKCU:\Console")) {
            Write-Verbose "OSDWinPE: Increase Console Screen Buffer"
            New-Item -Path "HKCU:\Console" -Force | Out-Null
            New-ItemProperty -Path HKCU:\Console ScreenBufferSize -Value 589889656 -PropertyType DWORD -Force | Out-Null
        }
        #=================================================
        # GetModules
        #=================================================
        if ($GetModules.IsPresent) {
            $GetPSDrive = Get-PSDrive -PSProvider 'FileSystem'
            foreach ($PSDrive in $GetPSDrive) {
                $OSDSearchPath = @("$($PSDrive.Root)Modules","$($PSDrive.Root)Content\Modules")
                foreach ($Item in $OSDSearchPath) {
                    if (Test-Path "$Item") {
                        Get-ChildItem "$Item" | `
                        Where-Object {$_.PSIsContainer} | `
                        ForEach-Object {
                            Write-Verbose "Copying Module at $($_.FullName) to X:\Program Files\WindowsPowerShell\Modules"
                            Copy-Item -Path "$($_.FullName)" -Destination "X:\Program Files\WindowsPowerShell\Modules" -Recurse -Force -ErrorAction SilentlyContinue
                            Import-Module -Name "$($_.Name)" -Force -ErrorAction SilentlyContinue
                        }
                    }
                }
            }
        }
        #=================================================
        # GetScripts
        #=================================================
        if ($GetScripts) {
            $GetPSDrive = Get-PSDrive -PSProvider 'FileSystem'

            foreach ($Item in $GetScripts) {
                foreach ($PSDrive in $GetPSDrive) {
                    $ScriptFullName = @("$($PSDrive.Root)$Item","$($PSDrive.Root)Content\Scripts\$Item")

                    foreach ($ScriptName in $ScriptFullName) {
                        if (Test-Path $ScriptName) {
                            $FoundScript = Get-Item $ScriptName | Select-Object -Property FullName
                            Write-Verbose "Executing PowerShell Script $($FoundScript.FullName)"
                            & "$($FoundScript.FullName)" -ErrorAction SilentlyContinue
                        }
                    }
                }
            }
        }
        #=================================================
        # wpeutil
        #=================================================
        if ($InitializeNetwork.IsPresent) {
            Write-Verbose 'OSDWinPE: wpeutil InitializeNetwork'
            Start-Process -WindowStyle Hidden -FilePath wpeutil -ArgumentList 'InitializeNetwork' -Wait
            Start-Sleep -Seconds 10
        }
        if ($InitializeNetworkNoWait.IsPresent) {
            Write-Verbose 'OSDWinPE: wpeutil InitializeNetwork /NoWait'
            Start-Process -WindowStyle Hidden -FilePath wpeutil -ArgumentList ('InitializeNetwork','/NoWait')
        }
        if ($WaitForNetwork.IsPresent) {
            Write-Verbose 'OSDWinPE: wpeutil WaitForNetwork'
            Start-Process -WindowStyle Hidden -FilePath wpeutil -ArgumentList 'WaitForNetwork' -Wait
        }
        if ($WaitForRemovableStorage.IsPresent) {
            Write-Verbose 'OSDWinPE: wpeutil WaitForRemovableStorage'
            Start-Process -WindowStyle Hidden -FilePath wpeutil -ArgumentList 'WaitForRemovableStorage' -Wait
        }
        if ($DisableFirewall.IsPresent) {
            Write-Verbose 'OSDWinPE: wpeutil DisableFirewall'
            Start-Process -WindowStyle Hidden -FilePath wpeutil -ArgumentList 'DisableFirewall' -Wait
        }
        if ($UpdateBootInfo.IsPresent) {
            Write-Verbose 'OSDWinPE: wpeutil UpdateBootInfo'
            Start-Process -WindowStyle Hidden -FilePath wpeutil -ArgumentList 'UpdateBootInfo'
        }
        #=================================================
        # Microsoft DaRT
        #=================================================
        if (($RemoteRecovery.IsPresent) -and (Test-Path "$env:windir\System32\RemoteRecovery.exe")) {
            Write-Verbose 'OSDWinPE: Microsoft DaRT Remote Recovery'
            Start-Process -WindowStyle Minimized -FilePath RemoteRecovery.exe -ArgumentList '-nomessage'
        }
        #=================================================
        # Reboot Shutdown
        #=================================================
        if ($Reboot.IsPresent) {
            Write-Verbose 'OSDWinPE: wpeutil Reboot'
            Start-Process -WindowStyle Hidden -FilePath wpeutil -ArgumentList 'Reboot'
        }
        if ($Shutdown.IsPresent) {
            Write-Verbose 'OSDWinPE: wpeutil Shutdown'
            Start-Process -WindowStyle Hidden -FilePath wpeutil -ArgumentList 'Shutdown'
        }
    }
    function Add-OfflineServicingWindowsDriver {
        [CmdletBinding()]
        param (
            [string]$Path = 'C:\Drivers'
        )
$UnattendXml = @"
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="offlineServicing">
        <component name="Microsoft-Windows-PnpCustomizationsNonWinPE" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <DriverPaths>
                <PathAndCredentials wcm:keyValue="1" wcm:action="add">
                    <Path>$Path</Path>
                </PathAndCredentials>
            </DriverPaths>
        </component>
        <component name="Microsoft-Windows-PnpCustomizationsNonWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <DriverPaths>
                <PathAndCredentials wcm:keyValue="1" wcm:action="add">
                    <Path>$Path</Path>
                </PathAndCredentials>
            </DriverPaths>
        </component>
    </settings>
</unattend>
"@

        #=================================================
        # Block
        #=================================================
        Block-WinOS
        Block-WindowsVersionNe10
        Block-PowerShellVersionLt5    
        #=================================================
        # Use-WindowsUnattend
        #=================================================
        $Random = Get-Random
        $UnattendXml | Out-File -FilePath "$env:TEMP\$Random.xml" -Encoding utf8 -Width 2000 -Force
        Use-WindowsUnattend -Path 'C:\' -UnattendPath "$env:TEMP\$Random.xml"
        #=================================================
    }
    function Use-WinPEContent {
        [CmdletBinding()]
        param (
            [ValidateSet('*','Drivers','Files','Modules','Registry','Scripts')]
            [string[]]$Content = '*'
        )
        #=================================================
        # Blocks
        #=================================================
        Block-WinOS
        #=================================================
        # PSDrive
        #=================================================
        $GetPSDrive = Get-PSDrive -PSProvider 'FileSystem'
    
        foreach ($Item in $Content) {
            #=================================================
            # Drivers
            #=================================================
            if ($Item -eq '*' -or $Item -eq 'Drivers') {
                foreach ($PSDrive in $GetPSDrive) {
                    $ContentPath = @("$($PSDrive.Root)Content\Drivers","$($PSDrive.Root)WinPE\Drivers")
                    foreach ($ContentItem in $ContentPath) {
                        if (Test-Path "$ContentItem") {
                            Get-ChildItem "$ContentItem" *.inf -Recurse | `
                            ForEach-Object {
                                Write-Verbose "Importing Driver $($_.FullName)"
                                PNPUtil.exe /add-driver "$($_.FullName)" /install
                            }
                        }
                    }
                }
            }
            #=================================================
            # Files
            #=================================================
            if ($Item -eq '*' -or $Item -eq 'Files') {
                foreach ($PSDrive in $GetPSDrive) {
                    $ContentPath = @("$($PSDrive.Root)Content\Files","$($PSDrive.Root)WinPE\Files")
                    foreach ($ContentItem in $ContentPath) {
                        if (Test-Path "$ContentItem") {
                            Write-Verbose "Copying Files at $ContentItem to X:\"
                            robocopy "$ContentItem" X:\ *.* /e /ndl /b
                        }
                    }
                }
            }
            #=================================================
            # Modules
            #=================================================
            if ($Item -eq '*' -or $Item -eq 'Modules') {
                foreach ($PSDrive in $GetPSDrive) {
                    $ContentPath = @("$($PSDrive.Root)Content\Modules","$($PSDrive.Root)WinPE\Modules")
                    foreach ($ContentItem in $ContentPath) {
                        if (Test-Path "$ContentItem") {
                            Get-ChildItem "$ContentItem" | `
                            Where-Object {$_.PSIsContainer} | `
                            ForEach-Object {
                                Write-Verbose "Copying Module at $($_.FullName) to X:\Program Files\WindowsPowerShell\Modules"
                                Copy-Item -Path "$($_.FullName)" -Destination "X:\Program Files\WindowsPowerShell\Modules" -Recurse -Force -ErrorAction SilentlyContinue
                                Import-Module -Name "$($_.Name)" -Force -ErrorAction SilentlyContinue
                            }
                        }
                    }
                }
            }
            #=================================================
            # Registry
            #=================================================
            if ($Item -eq '*' -or $Item -eq 'Registry') {
                foreach ($PSDrive in $GetPSDrive) {
                    $ContentPath = @("$($PSDrive.Root)Content\Registry","$($PSDrive.Root)WinPE\Registry")
                    foreach ($ContentItem in $ContentPath) {
                        if (Test-Path "$ContentItem") {
                            Get-ChildItem "$ContentItem" *.reg -Recurse | `
                            ForEach-Object {
                                Write-Verbose "Importing Registry File $($_.FullName)"
                                reg import "$($_.FullName)"
                            }
                        }
                    }
                }
            }
            #=================================================
            # Scripts
            #=================================================
            if ($Item -eq '*' -or $Item -eq 'Scripts') {
                foreach ($PSDrive in $GetPSDrive) {
                    $ContentPath = @("$($PSDrive.Root)Content\Scripts","$($PSDrive.Root)WinPE\Scripts")
                    foreach ($ContentItem in $ContentPath) {
                        if (Test-Path "$ContentItem") {
                            Get-ChildItem "$ContentItem" *.ps1 -Recurse | `
                            ForEach-Object {
                                Write-Verbose "Executing PowerShell Script $($_.FullName)"
                                & "$($_.FullName)" -ErrorAction SilentlyContinue
                            }
                        }
                    }
                }
            }
        }
    }
}