cloud/modules/_winpe.psm1
<#
.SYNOPSIS OSDCloud Cloud Module for functions.osdcloud.com .DESCRIPTION OSDCloud Cloud Module for functions.osdcloud.com .NOTES Version 22.6.9.1 .LINK https://raw.githubusercontent.com/OSDeploy/OSD/master/cloud/modules/_winpe.psm1 .EXAMPLE Invoke-Expression (Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/OSDeploy/OSD/master/cloud/modules/_winpe.psm1') #> #================================================= #region Functions function osdcloud-WinpeInstallCurl { [CmdletBinding()] param () if (-not (Get-Command 'curl.exe' -ErrorAction SilentlyContinue)) { Write-Host -ForegroundColor DarkGray 'Install Curl' $Uri = 'https://curl.se/windows/dl-7.81.0/curl-7.81.0-win64-mingw.zip' Invoke-WebRequest -UseBasicParsing -Uri $Uri -OutFile "$env:TEMP\curl.zip" $null = New-Item -Path "$env:TEMP\Curl" -ItemType Directory -Force Expand-Archive -Path "$env:TEMP\curl.zip" -DestinationPath "$env:TEMP\curl" Get-ChildItem "$env:TEMP\curl" -Include 'curl.exe' -Recurse | foreach {Copy-Item $_ -Destination "$env:SystemRoot\System32\curl.exe"} } } function osdcloud-WinpeInstallNuget { [CmdletBinding()] param () Write-Host -ForegroundColor DarkGray 'Install Nuget' $NuGetClientSourceURL = 'https://nuget.org/nuget.exe' $NuGetExeName = 'NuGet.exe' $PSGetProgramDataPath = Join-Path -Path $env:ProgramData -ChildPath 'Microsoft\Windows\PowerShell\PowerShellGet\' $nugetExeBasePath = $PSGetProgramDataPath if (-not (Test-Path -Path $nugetExeBasePath)) { $null = New-Item -Path $nugetExeBasePath -ItemType Directory -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } $nugetExeFilePath = Join-Path -Path $nugetExeBasePath -ChildPath $NuGetExeName $null = Invoke-WebRequest -UseBasicParsing -Uri $NuGetClientSourceURL -OutFile $nugetExeFilePath $PSGetAppLocalPath = Join-Path -Path $env:LOCALAPPDATA -ChildPath 'Microsoft\Windows\PowerShell\PowerShellGet\' $nugetExeBasePath = $PSGetAppLocalPath if (-not (Test-Path -Path $nugetExeBasePath)) { $null = New-Item -Path $nugetExeBasePath -ItemType Directory -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } $nugetExeFilePath = Join-Path -Path $nugetExeBasePath -ChildPath $NuGetExeName $null = Invoke-WebRequest -UseBasicParsing -Uri $NuGetClientSourceURL -OutFile $nugetExeFilePath } function osdcloud-WinpeInstallPowerShellGet { [CmdletBinding()] param () $InstalledModule = Import-Module PowerShellGet -PassThru -ErrorAction Ignore if (-not (Get-Module -Name PowerShellGet -ListAvailable | Where-Object {$_.Version -ge '2.2.5'})) { Write-Host -ForegroundColor DarkGray 'Install PowerShellGet' $PowerShellGetURL = "https://psg-prod-eastus.azureedge.net/packages/powershellget.2.2.5.nupkg" Invoke-WebRequest -UseBasicParsing -Uri $PowerShellGetURL -OutFile "$env:TEMP\powershellget.2.2.5.zip" $null = New-Item -Path "$env:TEMP\2.2.5" -ItemType Directory -Force Expand-Archive -Path "$env:TEMP\powershellget.2.2.5.zip" -DestinationPath "$env:TEMP\2.2.5" $null = New-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\PowerShellGet" -ItemType Directory -ErrorAction SilentlyContinue Move-Item -Path "$env:TEMP\2.2.5" -Destination "$env:ProgramFiles\WindowsPowerShell\Modules\PowerShellGet\2.2.5" Import-Module PowerShellGet -Force -Scope Global } } function osdcloud-WinpeSetEnvironmentVariables { [CmdletBinding()] param () if (Get-Item env:LocalAppData -ErrorAction Ignore) { Write-Verbose 'System Environment Variable LocalAppData is already present in this PowerShell session' } else { Write-Host -ForegroundColor DarkGray 'Set LocalAppData in System Environment' Write-Verbose 'WinPE does not have the LocalAppData System Environment Variable' Write-Verbose 'This can be enabled for this Power Session, but it will not persist' Write-Verbose 'Set System Environment Variable LocalAppData for this PowerShell session' #[System.Environment]::SetEnvironmentVariable('LocalAppData',"$env:UserProfile\AppData\Local") [System.Environment]::SetEnvironmentVariable('APPDATA',"$Env:UserProfile\AppData\Roaming",[System.EnvironmentVariableTarget]::Process) [System.Environment]::SetEnvironmentVariable('HOMEDRIVE',"$Env:SystemDrive",[System.EnvironmentVariableTarget]::Process) [System.Environment]::SetEnvironmentVariable('HOMEPATH',"$Env:UserProfile",[System.EnvironmentVariableTarget]::Process) [System.Environment]::SetEnvironmentVariable('LOCALAPPDATA',"$Env:UserProfile\AppData\Local",[System.EnvironmentVariableTarget]::Process) } } function osdcloud-WinpeUpdateDefender { <# Downloads the Defender Offline Update Kit, and applies to the OS. Functions adopted from the PowerShell File embedded in the zip file: DefenderUpdateWinImage.ps1 Orginal Script designed to work with WIM file, I've adopted it to work with Offline OS https://support.microsoft.com/en-us/topic/microsoft-defender-update-for-windows-operating-system-installation-images-1c89630b-61ff-00a1-04e2-2d1f3865450d #> Write-Host "Updating Defender Kit" -ForegroundColor Cyan function Get-PackageDetailsFromXml([string]$XmlPath) { if (!(Test-Path -Path $XmlPath)) { $global:LASTEXITCODE = $E_INVALIDPATH throw "Invalid path $XmlPath" } [xml]$xmlDoc = Get-Content -Path $XmlPath $pkgDetails = @{Architecture = $xmlDoc.packageinfo.arch; PackageVersion = $xmlDoc.packageinfo.versions.defender; CampVersion = $xmlDoc.packageinfo.versions.platform; EngineVersion = $xmlDoc.packageinfo.versions.engine; SignatureVersion = $xmlDoc.packageinfo.versions.signatures} return $pkgDetails } function ValidateCodeSign([string]$PackageFile) { $signInfo = Get-AuthenticodeSignature $PackageFile if ($signInfo."Status" -ne "Valid") { Write-Host ($messages.ERR_INVALID_SIGNATURE -f $PackageFile) -ForegroundColor Red $signInfo | Format-List $global:LASTEXITCODE = $E_INVALIDPACKAGE throw ($messages.ERR_INVALID_SIGNATURE -f $PackageFile) } } function Show-Update { # Check if there exists any update already applied. $mountPoint = $Image $installedPkgXml = Join-Path -Path $mountPoint -ChildPath $WindowsTemp | Join-Path -ChildPath $PackageXml if (!(Test-Path -Path $installedPkgXml)) { Write-Host ($messages.INFO_NO_UPDATE_IN_IMAGE) -ForegroundColor Yellow } else { # Get package details to output. $pkgDetails = Get-PackageDetailsFromXml -XmlPath $installedPkgXml Write-Host ($messages.INFO_PACKAGE_DETAILS -f $pkgDetails.PackageVersion, $pkgDetails.SignatureVersion, $pkgDetails.EngineVersion, $pkgDetails.CampVersion) -ForegroundColor Yellow } } function Add-Update([string]$WorkingDir, [string]$Image, [string]$PkgFile) { # Validate DISM package Code signing information ValidateCodeSign -PackageFile $PkgFile # Validate Working Directory if (Test-Path -path $WorkingDir) { if (!(Get-ChildItem $WorkingDir | Measure-Object).Count -eq 0) { $global:LASTEXITCODE = $E_INVALIDINPUT throw ($messages.ERR_WORKINGDIR_NOT_EMPTY -f $WorkingDir) } } $mountPoint = $Image try { # Extract Cab $cabContent = Join-Path -Path $WorkingDir -ChildPath "cab" New-Item -itemtype directory -path $cabContent -Force | Out-Null $Expand = expand $PkgFile -F:* $cabContent | Out-Null } catch { Write-Host "Failed to Extract Cab" -ForegroundColor Red throw } try { # Definition updates Write-Host ($messages.INFO_UPDATE_ENGINE_SIGN) -ForegroundColor Yellow $defSrc = Join-Path -Path $cabContent -ChildPath "Definition Updates\Updates" $defTarget = Join-Path -Path $mountPoint -ChildPath $DefinitionsUpdatesLocation if (!(Test-Path -Path $defTarget)) { New-Item -itemtype directory -path $defTarget | Out-Null } Copy-Item -Path "$defSrc\*" -Destination $defTarget -Recurse -Force # Platform updates Write-Host ($messages.INFO_UPDATE_CAMP) -ForegroundColor Yellow $campSrc = Join-Path -Path $cabContent -ChildPath "Platform" $campTarget = Join-Path -Path $mountPoint -ChildPath $PlatformLocation if (!(Test-Path -Path $campTarget)) { New-Item -itemtype directory -path $campTarget | Out-Null } Copy-Item -Path "$campSrc\*" -Destination $campTarget -Recurse -Force $campVersionFolder = Join-Path -Path $cabContent -ChildPath "Platform" $campVersionFolder = Get-ChildItem -Path $campVersionFolder -Directory -Name # Add Package Xml to Windows\Temp. $temp = Join-Path -Path $mountPoint -ChildPath $WindowsTemp $pkgXmlPath = Join-Path -Path $cabContent -ChildPath $PackageXml Copy-Item -Path $pkgXmlPath -Destination $temp -Force $global:LASTEXITCODE = $S_OK } catch { Write-Host ($messages.ERR_ADD_UPDATE) -ForegroundColor Red Write-Host $_ -ForegroundColor Yellow Write-Host $_.ScriptStackTrace -ForegroundColor Yellow Write-Host ($messages.INFO_DISCARD_IMAGE_UPDATE) Dismount-WindowsImage -Path $mountPoint -Discard | Out-Null throw } finally { Remove-Item -Path "$WorkingDir\*" -Recurse } } $uri = "https://go.microsoft.com/fwlink/?linkid=2144531" $Intermediate = "$env:TEMP\DefenderScratchSpace" $WorkingDir = "$Intermediate\WorkingFolder" $ProgramDataDefender = "ProgramData\Microsoft\Windows Defender" $ProgramFilesDefender = "Program Files\Windows Defender" $ProgramFilesX86Defender = "Program Files (x86)\Windows Defender" $DefinitionsUpdatesLocation = Join-Path -Path $ProgramDataDefender -ChildPath "Definition Updates\Updates" $PlatformLocation = Join-Path -Path $ProgramDataDefender -ChildPath "Platform" $WindowsTemp = "Windows\Temp" $PackageXml = "package-defender.xml" $PackageFile = "$Intermediate\Extract\defender-dism-x64.cab" $Image = "C:\" $messages = @{ #error messages ERR_INVALID_SIGNATURE = "Update package ({0}) does not have a valid signature. Redownload the package."; ERR_UNSUPPORTED_OS_VERSION = "Unsupported Windows OS image (version={0}). Windows 10 {1} or later versions are supported."; ERR_UNSUPPORTED_OS_UPDATE = "Unsupported Windows 10 version. For Windows 10 RS (Redstone) images, apply the September 2018, or later, update and retry."; ERR_UNSUPPORTED_ARC_PACKAGE = "Wrong OS architecture for this package. This package supports {0}."; ERR_UNSUPPORTED_OS_ARC = "Unsupported architecture of OS Image to service."; ERR_ADD_UPDATE = "Failed to add the Defender update."; ERR_REMOVE_UPDATE = "Failed to remove the Defender update."; ERR_NO_UPDATE_TO_REMOVE = "There's no Defender update in this image."; ERR_SHOW_UPDATE = "ShowUpdate failed"; ERR_COMMAND_MISSING = "Critical command `"{0}`" is missing. Install the required module and try again."; ERR_WORKINGDIR_NOT_EMPTY = "The input working directory ({0}) is not empty. Clear it or select an empty directory and retry."; ERR_UPDATE_ALREADY_EXISTS = "This image already contains another Defender update (version={0}). Run RemoveUpdate to clear it and try again"; ERR_FAILED_ENABLE_DEFENDER = "Failed to enable Defender in image ({1}) due to error ({0})." #info messages INFO_SERVER_ENABLE_DEFENDER = "Enabling Windows-Defender on the server image."; INFO_UPDATE_ENGINE_SIGN = "Updating security intelligence and antimalware engine."; INFO_UPDATE_CAMP = "Updating platform."; INFO_HANDLELING_LOCALIZATION = "Handling localization for {0}"; INFO_PACKAGE_DETAILS = "Details of Defender update applied to the image are:`n`tDefender package version: {0}`n`tSecurity intelligence version: {1}`n`tEngine version: {2}`n`tPlatform version: {3}"; INFO_DISCARD_IMAGE_UPDATE = "Discarding the changes and returning the OS image to its original state."; INFO_ADD_UPDATE_SUCCESS = "Successfully updated Defender."; INFO_REMOVE_UPDATE_SUCCESS = "Successfully removed the Defender update."; INFO_NO_UPDATE_IN_IMAGE = "This image doesn't have a Defender update applied."; INFO_IMAGE_UPDATE_DETAILS = "Details of the Defender update package in this image:" #warnings } if(!(Test-Path -Path "$Intermediate")) { $Null = New-Item -Path "$env:TEMP" -Name "DefenderScratchSpace" -ItemType Directory -Force } if(!(Test-Path -Path "$WorkingDir")) { $Null = New-Item -Path "$Intermediate" -Name "WorkingFolder" -ItemType Directory -Force } #Download Defender Kit File Write-Output "Starting Defender Kit Download" $Dest = "$Intermediate\" + 'defender-update-kit-x64.zip' $DefenderDef = Save-WebFile -SourceUrl $uri -DestinationDirectory $Intermediate -DestinationName 'defender-update-kit-x64.zip' if(Test-Path -Path $Dest) { Expand-Archive -Path $Dest -DestinationPath "$Intermediate\Extract" -Force Add-Update -WorkingDir $WorkingDir -Image $Image -PkgFile $PackageFile Show-Update } else {Write-Output "Failed Defender Kit Download"} } #endregion #================================================= |