Windows 10 Offline Servicing

PowerShell-Scripte für die schnelle Bereitstellung von aktuell gepatchten Installationsmedien von Window 10 Build 1909. Die Formate .WIM und .ISO sind teils Microsoft- und teils allgemeinspezifisch.

Ziel #1 - Erstellen einer aktuell gepatchten Windows 10-Ausgabe

Ausgangslage: IT-Profressionals stehen möglicherweise vor der Aufgabe, das Client-Betriebssystem Windows 10 laufend auf einem aktuell gepatchten Level zu bringen oder zumindest ein sogenanntes "Referenz-Image" zyklisch auf die Endgeräte "auszurollen". Monatlich erscheinen für Windows 10 Sicherheitsverbesserungen und Stabilitätsupdates (siehe Update-Verlauf). Diese müssen aufgrund von Compliance-Richtlinien (ISO 27001, NIST, STIG, BSI) sowie aus regulatorischen Gründen (DS-GVO, UGB etc.) in Organisationen, Behörden, Konzernen, Vereinen etc. angewendet werden. Das Update-Prozedere kontinuierlich (PDCA) zu ser­vi­cie­ren, fällt aufgrund der Komplexität vielen Akteuren nicht leicht - angesichts der täglichen vielen Routinen und Projekt-Paketen von IT-Verantwortlichen. Ansätze zur Semi- oder Vollautomatisierung sind gefragt. Eine Semi-Automatisierung, wie die folgenden PowerShell-Scripte, unterstützt bei der Reduzierung der Komplexität, damit ein kontinuierlichen Patch-Management von Microsoft Windows-10-Betriebssystemen gewährleistet werden kann.

Vorteile der scriptbasierten Variante

  • Transparenz, keine Kosten: Quelloffen, frei verfügbar und skalierbar für Endanwender.

  • Standard: Abbilder werden zu 100-Prozent-Hersteller-Empfehlung und der Erfahrung der Authoren wie Johan Arwidmark und Alexander Scharmer "lege artis" erzeugt.

  • Aktualität: Die aktuellsten SSU- und LCU-Updates werden automatisch - mit Unterstützung des PowerShell-Moduls OSDSUS/OSDUpdate von David Segura - direkt Online von der Microsoft Update-Cataloge-Site bezogen und integriert.

  • Optimiert "Small Footprint": Die Abbilder sind nach den Richtlinien des Herstellers und der WIM-Technologie auf geringen "Overhead" und kleinen Dateigrößen optimiert.

  • Simplifiziert: Das PowerShell-Script Create-W10RefImagev1909.ps1 ist nur auf das notwendigste reduziert, um ein natürliches und aktuelles Windows 10 Image zu erzeugen. Es verändert keine kritischen Komponenten oder ersetzt diese. Es werden auch keine unnötigen Dienste oder Services aktiviert oder deaktiviert.

Voraussetzungen

Es wird eine Verbindung zum Internet benötigt. Das Herunterladen der PowerShell-Module sowie der Software-Patches erfolgt auf Ressourcen in der Cloud. Stellen Sie deshalb eine Internet-Konnektivität sicher.

Der PowerShell-Oneliner meldet die erfolgreiche Internet-Konnektivität zurück:

if (Test-Connection -Count 1 -Quiet 1.1.1.1) {Write-Output "Internet-Konnektivität erfolgreich hergestellt."}

Benutzen Sie eine Privileged Access Workstations (PAWs), wo Sie administrative Rechte besitzen um Programm-Installationen vornehmen zu können. Dies kann ein Notebook, ein Desktop-PC oder sonstiges Endgerät mit Windows 10 Home, Pro, Education, Enterprise (in deutscher Sprachvariante) sein.

Bereitstellungsumgebung für Offline-Patchen einrichten

Benutzen Sie nachfolgendes PowerShell-Script mit der Bezeichnung Install-SetupDeploymentv1909.ps1für die initiale Bereitstellung für das Offline-Patchen von Windows 10. Die Erstkonfiguration erstellt neben der Ordnerstruktur C:\SetupDeployment ebenfalls die Installation der Microsoft Windows ADK-Tools in der Version 1903.

Folgende Komponenten umfasst die Script-Installation auf dem PAW-Endgerät

  1. Windows Assessment Toolkit and the Windows Performance Toolkit to assess the quality and performance of systems or components.

  2. Deployment tools such as WinPE, Sysprep, and other tools that you can use to customize and deploy Windows 10 images.

  3. Windows Preinstallation Environment (PE).

  4. Download the Windows System Image Manager (WSIM) 1903 update (fix).

Microsoft stellt für Windows 10 Build 1909 kein ADK in der Version 1909 bereit, es genügt 1903 zu nutzen.

Empfehlung für den korrekten Aufruf des PS Install-SetupDeploymentv1909.ps1 ist:

powershell -ex bypass -co <pfad zum PS Script Install-SetupDeploymentv1909.ps1>

Die obige Befehlszeile in einer privilegierten Kommandozeile (CMD) unter Windows 10 aufrufen (z. B. als Administrator).

Nach dem Aufruf mit privilegierten Berechtigungen wird die Installation aller Komponenten automatisch durchgeführt. Dies kann je nach Ressourcen bzw. Leistung des Endgerätes zwischen 15 und 45 Minuten dauern - bitte haben Sie hier etwas Geduld.

Source-Code des Scripts Install-SetupDeploymentv1909.ps1

Install-SetupDeploymentv1909.ps1
# Check for elevation
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
[Security.Principal.WindowsBuiltInRole] "Administrator"))
{
Write-Warning "Oupps, you need to run this script from an elevated PowerShell prompt!`nPlease start the PowerShell prompt as an Administrator and re-run the script."
Write-Warning "Aborting script..."
Break
}
# Our Deployment Folder Structure
$deploy_dir = "$env:HOMEDRIVE\SetupDeployment"
$deploy_DismPath = "$env:HOMEDRIVE\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM"
$deploy_OscdImgPath = "$env:HOMEDRIVE\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg"
$deploy_src = "$deploy_dir\src"
$deploy_drivers = "$deploy_dir\src\drivers"
$deploy_ADKOffline = "$deploy_dir\src\ADKOffline"
$deploy_ADKWinPeOffline = "$deploy_dir\src\ADKWinPeOffline"
# $deploy_UpdatesDir = "$deploy_dir\src\Updates"
$deploy_logs = "$deploy_dir\logs"
$deploy_build = "$deploy_dir\build"
$deploy_mount = "$deploy_dir\build\mount"
$deploy_iso = "$deploy_dir\ISO"
$deploy_tmp = "$deploy_dir\tmp"
$deploy_WSIMUpdate = "$deploy_dir\src\WSIM1903Update"
if (!(Test-Path -path $deploy_dir)) {New-Item $deploy_dir -Type Directory}
if (!(Test-Path -path $deploy_src)) {New-Item $deploy_src -Type Directory}
if (!(Test-Path -path $deploy_drivers)) {New-Item $deploy_drivers -Type Directory}
if (!(Test-Path -path $deploy_ADKOffline)) {New-Item $deploy_ADKOffline -Type Directory}
if (!(Test-Path -path $deploy_ADKWinPeOffline)) {New-Item $deploy_ADKWinPeOffline -Type Directory}
# if (!(Test-Path -path $deploy_UpdatesDir)) {New-Item $deploy_UpdatesDir -Type Directory}
# if (!(Test-Path -path $deploy_UpdatesDirLCU)) {New-Item $deploy_UpdatesDirLCU -Type Directory}
# if (!(Test-Path -path $deploy_UpdatesDirSSU)) {New-Item $deploy_UpdatesDirSSU -Type Directory}
if (!(Test-Path -path $deploy_WSIMUpdate)) {New-Item $deploy_WSIMUpdate -Type Directory}
if (!(Test-Path -path $deploy_logs)) {New-Item $deploy_logs -Type Directory}
if (!(Test-Path -path $deploy_build)) {New-Item $deploy_build -Type Directory}
if (!(Test-Path -path $deploy_mount)) {New-Item $deploy_mount -Type Directory}
if (!(Test-Path -path $deploy_tmp)) {New-Item $deploy_tmp -Type Directory}
if (!(Test-Path -path $deploy_iso)) {New-Item $deploy_iso -Type Directory}
# A Windows ADK for Windows 10, version 1909 will not be released. You can use the Windows ADK for Windows 10, version 1903 to deploy Windows 10, version 1909.
# https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install
# ADK Version 1903 Url and Install Options
# https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install
$adk_url = 'https://download.microsoft.com/download/B/E/6/BE63E3A5-5D1C-43E7-9875-DFA2B301EC70/adk/adksetup.exe'
$adk_file = 'adksetup.exe'
$adk_features = '*' # https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-8.1-and-8/dn621910(v=win.10)
$adk_install_log = "$deploy_logs\adksetup.log"
$adk_reg_key = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{fb450356-9879-4b2e-8dc9-282709286661}"
$adk_winPeUrl = "https://download.microsoft.com/download/E/F/A/EFA17CF0-7140-4E92-AC0A-D89366EBD79E/adkwinpeaddons/adkwinpesetup.exe"
$adk_winPeFile = "adkwinpesetup.exe"
$mdt_reg_key = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{2E6CD7B9-9D00-4B04-882F-E6971BC9A763}"
$mdt_fileURL = "https://download.microsoft.com/download/3/3/9/339BE62D-B4B8-4956-B58D-73C4685FC492/MicrosoftDeploymentToolkit_x64.msi"
$mdt_fileName = "MicrosoftDeploymentToolkit_x64.msi"
$adk_WinPeInstall_log= "C:\SetupDeployment\logs\ADKWinPEInstallation.log"
Add-Type -AssemblyName "system.io.compression.filesystem"
Start-Sleep -Seconds 2
if(-not (Test-Path -Path $adk_reg_key))
{
# Offline Download ADK
# $downADK = (New-Object System.Net.WebClient).DownloadFile($adk_url,"$deploy_src\$adk_file")
# Invoke-WebRequest -uri $adk_url -OutFile $deploy_src\$adk_file -PassThru -Verbose
Write-Host "`nDownload von $adk_url erfolgt ..." -ForeGroundColor Green
Start-BitsTransfer -Source $adk_url -Destination "$deploy_src\$adk_file"
Write-Host "`nInstallation von $deploy_src\$adk_file erfolgt ..." -ForeGroundColor Green
Start-Process -FilePath "$deploy_src\$adk_file" -ArgumentList "/quiet /layout $deploy_ADKOffline /log `"$adk_install_log`"" -wait -Verbose
[System.GC]::Collect()
# Offline Download WinPE-ADK
# $downADKPE = (New-Object System.Net.WebClient).DownloadFile($adk_winPeUrl,"$deploy_src\$adk_winPeFile")
# Invoke-WebRequest -UseBasicParsing -uri $adk_winPeUrl -OutFile $deploy_src\$adk_winPeFile -PassThru -Verbose
Write-Host "`nDownload von $adk_winPeUrl erfolgt ..." -ForeGroundColor Green
Start-BitsTransfer -Source $adk_winPeUrl -Destination "$deploy_src\$adk_winPeFile"
Write-Host "`nInstallation von $deploy_src\$adk_winPeFile erfolgt ..." -ForeGroundColor Green
Start-Process -FilePath "$deploy_src\$adk_winPeFile" -ArgumentList "/quiet /layout $deploy_ADKWinPeOffline /log `"$adk_WinPeInstall_log`"" -wait -Verbose
[System.GC]::Collect()
# Install ADK Offline-Repository
Write-Host "`nInstallation von Windows ADK erfolgt ..." -ForeGroundColor Green
Start-Process -FilePath "$deploy_ADKOffline\$adk_file" -ArgumentList "/quiet /norestart /log `"$adk_install_log`"" -wait -PassThru -Verbose
# Install WinPE Offline-Repository
Write-Host "`nInstallation von Windows PE erfolgt ..." -ForeGroundColor Green
Start-Process -FilePath "$deploy_ADKWinPeOffline\$adk_winPeFile" -ArgumentList "/quiet /norestart /log `"$adk_WinPeInstall_log`"" -wait -PassThru -Verbose
# Download and Install the Windows System Image Manager (WSIM) 1903 update
$WSIMUpdateURL = "https://download.microsoft.com/download/4/0/F/40FD29FC-0E6D-46D0-8F7E-B033110D07D5/WSIM1903.zip"
Write-Host "`nDownload von Windows System Image Manager (WSIM) 1903 update erfolgt ..." -ForeGroundColor Green
Start-BitsTransfer -Source $WSIMUpdateURL -Destination "$deploy_src\WSIM1903Update\WSIM1903.zip"
[io.compression.zipfile]::ExtractToDirectory("$deploy_src\WSIM1903Update\WSIM1903.zip", "$deploy_src/WSIM1903Update/") | Out-Null
Write-Host "`nInstallation von Windows System Image Manager (WSIM) 1903 update erfolgt ..." -ForeGroundColor Green
Start-Process -FilePath "cmd.exe" -ArgumentList "/c $deploy_src\WSIM1903Update\UpdateWSIM.bat" -WindowStyle Hidden -WorkingDirectory "$deploy_src/WSIM1903Update/" -PassThru -Wait
}
# Install MDT 8456
if(-not (Test-Path -Path $mdt_reg_key)) {
Write-Host "`nDownload von $mdt_fileName erfolgt ..." -ForeGroundColor Green
Start-BitsTransfer -Source $mdt_fileURL -Destination "$deploy_src\$mdt_fileName"
# Invoke-WebRequest -UseBasicParsing -uri $mdt_fileURL -OutFile $deploy_src\$mdt_fileName -PassThru -Verbose
Write-Host "`nInstallation von $deploy_src\$mdt_fileName erfolgt ..." -ForeGroundColor Green
Start-Process -FilePath "$deploy_src\$mdt_fileName" -ArgumentList "/passive /norestart /L*V `"$deploy_logs\mdt_install.log`"" -wait -PassThru -Verbose
# Download and extract Sysinternals
$SysinternalsURL = "https://download.sysinternals.com/files/SysinternalsSuite.zip"
Write-Host "`nDownload von $SysinternalsURL erfolgt ..." -ForeGroundColor Green
Start-BitsTransfer -Source $SysinternalsURL -Destination "$deploy_src\Sysinternals.zip"
Write-Host "`nEntpacken von $deploy_src\Sysinternals.zip erfolgt ..." -ForeGroundColor Green
[io.compression.zipfile]::ExtractToDirectory("$deploy_src\Sysinternals.zip", "$deploy_src/Sysinternals")
}
# Check if SSU OR LCU exists else get it
# if ((Get-ChildItem $deploy_UpdatesDirSSU -Filter *.msu | measure).Count -eq 0 `
# -OR (Get-ChildItem $deploy_UpdatesDirLCU -Filter *.msu | measure).Count -eq 0) {
# Import DISM modules from Windows 10 ADK
Import-Module $deploy_DismPath -Force
# Global Variables
& setx /M "PATH" "$ENV:PATH;$deploy_DismPath"
# Get Latest Updates Windows 10 Version 1903
# https://docs.stealthpuppy.com/docs/latestupdate/usage/get-stack
# Install-PackageProvider -Name NuGet -Force -Confirm:$false
# Install-Module -Name LatestUpdate -Force -Confirm:$false -AllowClobber
# Import-Module -Name LatestUpdate -Force -PassThru
# SSU
# Get-LatestServicingStackUpdate -OperatingSystem Windows10 -Version 1903 | `
# Where-Object { $_.Architecture -eq "x64" -and $_.Note -notmatch "Server"} | `
# Save-LatestUpdate -Path $deploy_UpdatesDirSSU
# LCU
# Get-LatestCumulativeUpdate -OperatingSystem Windows10 -Version 1903 | `
# Where-Object { $_.Architecture -eq "x64" -and $_.Note -notmatch "Server"} | `
# Save-LatestUpdate -Path $deploy_UpdatesDirLCU
# Unblock Files
# Get-ChildItem -Path $deploy_UpdatesDir -Filter *.msu -Recurse -Force | Unblock-File
# }
# Fix NTFS-permission
& icacls $deploy_dir /c /t /q /reset | Out-Null
# Fix MDT DeploymentShare permissions
# https://deploymentresearch.com/fixing-mdt-2013-update-1-deployment-share-permissions-using-powershell/
# ------------------------------------------------------------------------------------------

Video-Sequenz zum Ablauf des ersten Scripts.

Animated-GIF-Video: PS Install-SetupDeploymentv1909.ps1 zeigt die groben Abläufe der Ersteinrichtung.

Windows-Medien mit aktuellen SSU und LCU erstellen

Das PowerShell-Script Create-W10RefImagev1909.ps1 erstellt nach Durchlauf die aktuell gepatchte Windows-10-Version 1909 inklusive dem Feature .NET-Framework 2.0-3.5. Die Besonderheit ist die Reihenfolge in dem das Referenz-Image erstellt wird. Dies ist ein Abbild einer etablierten Herstellung eines sogenannten "Referenz"-Image wie es von Microsoft empfohlen wird.

1) Kopieren Sie das Script Create-W10RefImagev1909.ps1 innherhalb des Ordners C:\SetupDeployment\

Folgende Änderungen sollten Sie vor dem ersten Aufruf ggf. vornehmen:
$ISO = [System.IO.Path]::Combine("$RootDir\ISO","<Name der ISO-Datei.iso>")
Der Name ihrer ISO-Datei sollte im Pfad C:\SetupDeployment\ISO angegeben werden und existent sein.

Kopieren Sie also vor dem Aufrufen des PowerShell-Scripts ihre gewünschte ISO-Datei in den Ordner C:\SetupDeployment\ISO und halten Sie den Namen durch die Änderung der Variable $ISO im Script fest. Ansonsten erscheint das folgende Dialogfenster und bittet Sie um Angabe eines ISO-Abbildes um erfolgreich fortzusetzen zu können.

Vor Start des Scripts Create-W10RefImagev1909.ps1 eine Windows-10-ISO-Datei in den Ordner kopieren.

Ausführen des Scripts unter privilegierten Benutzerrechten:

2) powershell -ex bypass -co <pfad zum Script Create-W10RefImagev1909.ps1>

Beispiel: powershell -ex bypass -co "C:\SetupDeployment\Create-W10RefIMagev1909.ps1"

Glossar zu SSU und LCU

SSU = Servicing Stack-updates/Wartungsstapel: SSUs verbessern die Zuverlässigkeit des Updatevorgangs. Dadurch wird das Risiko potenzieller Probleme beim Installieren des LCU und beim Anwenden von Microsoft-Sicherheitsfixes reduziert. Beim Warten auf Stack-Updates werden Korrekturen für den Wartungsstapel, die Komponente, die Windows-Updates installiert, bereitgestellt. Darüber hinaus enthält Sie den "komponentenbasierten Servicing Stack" (CBS), der eine wichtige zugrunde liegende Komponente für verschiedene Elemente der Windows-Bereitstellung ist, wie DISM, SFC, Ändern von Windows-Features oder-Rollen sowie Reparieren von Komponenten. Das CBS ist eine kleine Komponente, in der in der Regel keine Updates monatlich veröffentlicht werden.

LCU = Latest Cumulative Update: A tested, cumulative set of updates. They include both security and reliability updates that are packaged together and distributed over Windows Update, WSUS, System Center Configuration Manager and Microsoft Update Catalog for easy deployment. The Monthly Rollup is product specific, addresses both new security issues and nonsecurity issues in a single update and will proactively include updates that were released in the past. Security vulnerabilities are rated by their severity. The severity rating is indicated in the Microsoft security bulletin as critical, important, moderate, or low. This Monthly Rollup would be displayed under the title Security Monthly Quality Rollup when you download or install. This Monthly Rollup will be classified as an "Important" update on Windows Update and will automatically download and install if your Windows Update settings are configured to automatically download and install Important updates.

Why does a Windows 10 Cumulative Update Install Twice?

Wenn ein kumulatives Update (LCU) zweimal installiert wird, können Sie die Hintergründe hier in diesem Artikel recherchieren.

Empfohlene Reihenfolge beim Offline-Service-Management

Das Script unterstützt die folgende Hersteller-Empfehlung in den folgenden Punkten:

  • "Ungepatchte" ISO-Datei von Microsoft über das Media-Creation-Tool herunterladen, berufsbildende mittlere und höher Schulen in AT nutzen die MSACH-Plattform und Unternehmen mit Volumen-Lizenzvertrag nutzen die Plattform Volume Licensing Service Center.

  • Export der aktuellen Basis in ein temporäres Repository.

  • Einbinden (Mount) des temporären Repositories für weitere Modifikationen.

  • SSU als Package - offline - hinzufügen.

  • LCU als Package - offline - hinzufügen.

  • Cleanup mit DISM.exe und den Schaltern /Resetbase /StartComponentCleanup.

  • Hinzufügen des Features .NET Framework 3.5.1.

  • LCU als Package - offline - erneut anwenden aufgrund der .NET Framework-Änderungen.

  • Aushängen (Dismount) bzw. Speichern des temporären Repository.

  • Erneut Export-Vorgang des Images durchführen um "Ballast" zu entfernen (siehe Microsoft OEM).

  • Löschen des temporären Repository.

  • Aktualisierte WIM-Image in Medienordner verschieben und ISO-Datei mit ADK-Tools generieren.

  • Aushängen bzw. Entbinden aller genutzten Dateien und Images.

Source Code Create-W10RefImagev1909.ps1

Create-W10RefImagev1909.ps1
<#
.SYNOPSIS
Dieses Script erstellt eine aktuell gepatchte ISO-Installations- und WIM-Datei von Windows 10 1909.
Führe das Script unter privilegierten Benutzerrechten (Administrator) aus um einen einwandfreien
Betrieb der Funktionen zu gewährleisten!
.DESCRIPTION
Für die schnelle Bereitstellung von Windows in Campus-Umgebungen sind aktuell gepatchte
ISO- und WIM-Dateien eine wesentliche Unterstützung. Die Weiterverarbeitung von WIM-Dateien sind
in MDT oder SCM-Tools von Microsoft direkt zu importieren.
Die ISO-Datei kann mit Tools wie Rufus (https://rufus.ie/) auf einen USB-Stick übertragen werden.
.PARAMETER <Parameter_Name>
No parameter input required.
.INPUTS
None
.OUTPUTS
WIM-file stored in C:\SetupDeployment\build\REFW10-001.wim
ISO-file stored in C:\SetupDeployment\build\<W10_<DATE>.ISO
.NOTES
Version: 2.0
Author: Alexander Scharmer
Creation Date: 29.12.2019
Purpose/Change:
Note #1 : To service a newer version of WinPE than the OS you are servicing from, for example service Windows 10 v1709
from a Windows Server 2019 server, you need a newer DISM version.
Solution, simply install the latest Windows ADK 10, and use DISM from that version
Note #2 : If your Windows OS already have a newer version of dism, uncomment the below line, and comment out line 11 and 12
$DISMFile = 'dism.exe'
.EXAMPLE
PS> . \Create-W10RefImagev1909.ps1
#>
Clear-Host
Set-ExecutionPolicy Bypass -Scope Process -Force
## Release handles on Registry Hive and free up memory
[System.GC]::Collect()
# Check for elevation
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
[Security.Principal.WindowsBuiltInRole] "Administrator"))
{
Write-Warning "Oupps, you need to run this script from an elevated PowerShell prompt!`nPlease start the PowerShell prompt as an Administrator and re-run the script."
Write-Warning "Aborting script..."
Break
Pause
}
write-host "`nOffline-Patching Window 10 Image v2.0 - by Alexander Scharmer.`n" -ForegroundColor Yellow -BackgroundColor Black
$DISMFile = 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe'
If (!(Test-Path $DISMFile))
{
$scriptName = $MyInvocation.MyCommand.Name
Write-Warning "DISM.exe bzw. Windows ADK nicht gefunden! Abbruch des Scripts $scriptName erfolgt!"
[console]::beep(1000,500)
Write-Host "`nINFO: Führen Sie das Script 'Install-SetupDeployment.ps1' vorher aus um alle Tools zu installieren und um die notwendige Ordnerstruktur zu erstellen." -ForegroundColor White -BackgroundColor Black
Break
}
else { Write-Host "`nDISM.exe in Windows ADK gefunden! Das Script wird fortgesetzt ..." -ForegroundColor Green }
$RootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path)
$ParentDir = [System.IO.Path]::GetDirectoryName($RootDir)
Set-StrictMode -Version Latest function fGetUpdates { [CmdletBinding()] Param( [parameter(Position=0,Mandatory=$true)] [String]$arch = "x64", [parameter(Position=1,Mandatory=$true)] [ValidateSet("SSU","LCU")] [String]$SLCU, [parameter(Position=2,Mandatory=$true)] [ValidateSet("1909","1903","1809","1803")] [String]$BuildNummer ) $ErrorActionPreference = "Stop"
try{
<#
In diesem Block der Code, der eine Exception auslösen könnte
#>
if (!(Get-Module "OSDSUS"))
{
write-host "Modul OSDSUS nicht geladen ... versuche es nachzuladen ..." -ForegroundColor DarkGreen
Import-Module OSDSUS -Force
}
if (!(Get-Module "OSDUpdate"))
{
write-host "Modul OSDUpdate nicht geladen ... versuche es nachzuladen ..." -ForegroundColor DarkGreen
Import-Module OSDUpdate -Force
}
# $error[0].Exception.GetType().FullName
}
catch [System.IO.FileNotFoundException]
{
<# Modul OSDSUS oder OSDUpdate wurde nicht gefunden weil nicht installiert #>
write-host "Modul OSDSUS bzw. OSDUpdate nicht installiert ... versuche es aus dem Internet zu laden und zu installieren ..." -ForegroundColor DarkGreen
Install-PackageProvider -Name NuGet -Force -Scope AllUsers -ForceBootstrap
Install-Module OSDSUS -Scope AllUsers -Force -AllowClobber -SkipPublisherCheck -Confirm:$false Update-OSDSUS
Import-Module OSDSUS -Force Install-Module OSDUpdate -Scope AllUsers -Force -AllowClobber -SkipPublisherCheck -Confirm:$false Import-Module OSDUpdate -Force
}
catch {
<#
Hier findet die Fehlerbehandlung statt, z.B. das Schreiben eines Logs
Der letzte aufgezeichnete Fehler ist hier über die Variable $_ abrufbar,
einzelne Eigenschaften daher nach diesem Muster: $_.Exception.Message
#>
"`n==> Exception Handling durch nicht-terminierenden Fehler"
}
finally{
<#
Jede Anweisung in diesem Block wird immer ausgeführt, egal ob ein
Fehler aufgetreten ist oder nicht. Dieser Block ist optional.
#>
write-host "`n `nFinally-Block-OSD-Module: Erfolgreich Verarbeitung." -ForegroundColor Green
if (Get-Module "OSDSUS") { Write-Host "Modul OSDSUS erfolgreich geladen." -ForegroundColor Yellow}
if (Get-Module "OSDUpdate") { Write-Host "Modul OSDUpdate erfolgreich geladen." -ForegroundColor Yellow}
# $error[0].Exception.GetType().FullName
$error.Clear()
}
# Get Update-Information $updateInfo = Get-OSDUpdate | ? {$_.UpdateOS -eq 'Windows 10' -and $_.UpdateBuild -eq $BuildNummer -and $_.UpdateGroup -eq $SLCU -and $_.UpdateArch -eq $arch -and $_.IsLatestRevision -eq 'True'} # Combine Path-Structure $path = [IO.Path]::Combine("$env:SystemDrive\SetupDeployment\Updates", $updateInfo."UpdateOS", $updateInfo."UpdateBuild", $updateInfo."Updategroup") # Create Folder-Structure for Updates New-Item $path -ItemType Directory -Force | Out-Null # Download or match Updates $updateInfo | Get-DownOSDUpdate -DownloadPath $path sl $path | Out-Null set-content -path .\LatestFile.txt -value ([IO.Path]::Combine($path, $updateInfo."Title", $updateInfo."FileName")) -Force } fGetUpdates -arch x64 -SLCU SSU -BuildNummer 1909 fGetUpdates -arch x64 -SLCU LCU -BuildNummer 1909
#Auxiliary
$ISO = [System.IO.Path]::Combine("$RootDir\ISO","SW_DVD9_Win_Pro_10_1909_64BIT_German_Pro_Ent_EDU_N_MLF_X22-17400.ISO")
$SSU = Get-Content -Path ([System.IO.Path]::Combine("$env:SystemDrive\SetupDeployment\Updates","Windows 10","1909","SSU","LatestFile.txt"))
$LCU = Get-Content -Path ([System.IO.Path]::Combine("$env:SystemDrive\SetupDeployment\Updates","Windows 10","1909","LCU","LatestFile.txt"))
$MountFolder = [System.IO.Path]::Combine($RootDir,'build\Mount')
$RefImageFolder = [System.IO.Path]::Combine($RootDir,'build')
$TmpImage = [System.IO.Path]::Combine("$RootDir\tmp",'tmp_install.wim')
$RefImage = [System.IO.Path]::Combine($RefImageFolder,'REFW10-001.wim')
# Verify that the ISO and CU files existnote
if (!(Test-Path -path $ISO))
{
Write-Warning "`nINFO: Could not find Windows 10 ISO $ISO. Wähle eine gültige ISO-Datei im Dialogfeld aus ..."
Write-Host "INFO: Im Regelfall sucht das Script eine Windows-10-ISO-Datei im Pfad $ISO" -ForegroundColor Green
Add-Type -AssemblyName System.Windows.Forms # https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.openfiledialog?redirectedfrom=MSDN&view=netframework-4.8
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
Title = "Wähle eine gültige Windows-10-ISO-Datei aus ..."
InitialDirectory = [System.IO.Directory]::GetCurrentDirectory();
Multiselect = $false
Filter = "Image-Dateien (*.iso)|*.iso";
CheckPathExists = 'True';
}
[void]$FileBrowser.ShowDialog()
$ISO = $FileBrowser.FileName
}
if (!(Test-Path -path $SSU))
{
Write-Warning "Could not find servicing stack Update for Windows 10. Try to download ..."
fGetUpdates -arch x64 -SLCU SSU -BuildNummer 1909
}
if (!(Test-Path -path $LCU))
{
Write-Warning "Could not find Cumulative Update for Windows 10. Try to download ..."
fGetUpdates -arch x64 -SLCU LCU -BuildNummer 1909
}
# Mount the Windows 10 ISO
Write-Host "`nTry Mounting ISO $ISO ..." -ForegroundColor Green
Mount-DiskImage -ImagePath $ISO | Out-Null
$ISOImage = Get-DiskImage -ImagePath $ISO | Get-Volume
$ISODrive = [string]$ISOImage.DriveLetter+":"
$ImageName = (Get-WindowsImage -ImagePath "$ISODrive\Sources\install.wim" | ? { $_.imageindex -eq '1' }).ImageName.toString()
# Export the Windows 10 Standard index 1 to a new WIM
Write-Host "`nExporting image $ImageName from $ISODrive\Sources\install.wim ..." -ForegroundColor Green
Export-WindowsImage -SourceImagePath "$ISODrive\Sources\install.wim" -SourceIndex 1 -DestinationImagePath $TmpImage
# Mount the image
Write-Host "`nMounting WIM-File $TmpImage ..." -ForegroundColor Green
Mount-WindowsImage -ImagePath $TmpImage -Index 1 -Path $MountFolder
# Add the latest SSU to the Windows 10 Standard image
Write-Host "`nInstalling SSU $SSU ..." -ForegroundColor Green
Add-WindowsPackage -PackagePath $SSU -Path $MountFolder
# Add the latest CU (LCU) to the Windows 10 Standard image
Write-Host "Installing LCU (bitte um Geduld, dauert etwas) $LCU ..." -ForegroundColor Green
Add-WindowsPackage -PackagePath $LCU -Path $MountFolder
# Cleanup the image BEFORE installing .NET to prevent errors
# Using the /ResetBase switch with the /StartComponentCleanup parameter of DISM.exe on a running version of Windows removes all superseded versions of every component in the component store.
# https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/clean-up-the-winsxs-folder#span-iddismexespanspan-iddismexespandismexe
Write-Host "`nCleaning up Image $MountFolder BEFORE installing .NET to prevent errors ..." -ForegroundColor Green
& $DISMFile /Image:$MountFolder /Cleanup-Image /StartComponentCleanup /ResetBase
# Add .NET Framework 3.5.1 to the Windows 10 Standard image
Write-Host "`nInstalling .Net Framework 3.5. to Offline-Image $MountFolder ..." -ForegroundColor Green
Add-WindowsCapability -Name NetFx3~~~~ –Source $ISODrive\sources\sxs\ -Path $MountFolder | Out-Null
# Re-apply latest CU (LCU) because of .NET changes
Write-Host "`nErneutes Applizieren von LCU - aufgrund von .NET 2.0-3.5-Package-Installation - erforderlich ..." -ForegroundColor Green
Add-WindowsPackage -PackagePath $LCU -Path $MountFolder
# Dismount the Windows 10 Standard image
Write-Host "`nDismounting WIM $MountFolder ..." -ForegroundColor Green
DisMount-WindowsImage -Path $MountFolder -Save
# Export the Windows 10 index to a new WIM (the export operation reduces the WIM size with about 100 MB or so)
# Optimize final image - https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/oem-deployment-of-windows-10-for-desktop-editions
# Remove unused packages from your image, by exporting a copy of it.
Write-Host "`nExporting the WIM-File $TmpImage to $RefImage ..." -ForegroundColor Green
Export-WindowsImage -SourceImagePath $TmpImage -SourceIndex "1" -DestinationImagePath $RefImage
# Remove the uneeded temporary WIM-File.
Write-Host "`nDelete temporary WIM $TmpImage ..." -ForegroundColor Green
if (Test-Path -path $TmpImage) {Remove-Item -Path $TmpImage -Force}
# Copy REF-IMG to Media for ISO-Build
Write-Host "`nCopy REF-IMG to Media for ISO-Build ..." -ForegroundColor Green
if (!(Test-Path -path "$RefImageFolder\media")) {New-Item -path "$RefImageFolder\media" -ItemType Directory}
Copy-Item -Path "$ISODrive\*" -Destination "$RefImageFolder\media" -Force -Recurse
Copy-Item -Path $RefImage -Destination "$RefImageFolder\Media\sources\install.wim" -Force -Filter "*.wim" -Confirm:$false
# Create Windows ISO
$ISOFile = "$RefImageFolder" + "\" + ([string]"W10_$(get-date -Format d)").Replace(".","_") + ".ISO"
write-Host "`nKonstruiere Dateiname für ISO-Datei $ISOFile" -ForegroundColor Green
$ToolsPath = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg"
$BootData='2#p0,e,b"{0}"#pEF,e,b"{1}"' -f "$ToolsPath\etfsboot.com","$ToolsPath\efisys_noprompt.bin"
$Proc = Start-Process -FilePath "$ToolsPath\oscdimg.exe" -ArgumentList @("-bootdata:$BootData",'-u2','-udfver102',"$RefImageFolder\media","$ISOFile") -PassThru -Wait
if($Proc.ExitCode -ne 0)
{
Throw "Failed to generate ISO with exitcode $($Proc.ExitCode)"
}
else {
Write-Host "`nMedia $ISOFile creation has been successfully done!" -ForegroundColor Yellow -BackgroundColor Black
}
# Dismount the Windows 10 ISO
Write-Host "Dismounting ISO $ISO und finalisiere Script ..." -ForegroundColor Green
Dismount-DiskImage -ImagePath $ISO | Out-Null
Write-Host "Ende. Have a nice Day!"
# END OF SCRIPT

Video-Sequenz zum Ablauf des ersten Scripts.

Animiertes GIF-Video: Abbild der groben Vorgänge des PS Create-W10RefImagev1909.ps1.

Ausgabe der Medienformate .ISO und .WIM

Als Ergebnis werden die aktuell gepatchten Windows-10-1909-Builds als Abbilder in den Formaten .WIM und .ISO im Ordner C:\SetupDeployment\build ausgegeben.

Das Endergebnis sind zwei Abbilder in den Formaten .WIM und .ISO.

Manuelles Erstellen eines startfähigen ISO-Abbildes von einem Ordner

Im Zusammenhang von Windows-Deployments werden auch Anforderungen gestellt, wo ein ISO-Abbild in einen Ordner kopiert wird, der Inhalt des Ordners aktualisiert wird (z. B. die Datei install.wim, oder das Einfügen einer Autounattend.xml-Datei oder auch Apps etc.) und anschließend wieder in das ISO-Format zurückgebracht werden soll. Microsoft liefert mit dem Windows ADK ein Kommandozeilen-basiertes Tool namens oscdimg.exe aus. Die 64-Bit-Variante von des Tools wird im Regelfall in den Standardordner C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe abgelegt. Dies kann unter Angabe verschiedener Parameter erneut ein ISO-Abbild generieren. Folgendes Beispiel soll dies skizzieren:

oscdimg.exe -u2 -udfver102 -bootdata:2#p0,e,bD:\Iso_Files\boot\etfsboot.com#pEF,e,bD:\Iso_Files\efi\microsoft\boot\efisys.bin D:\ISO_Files D:\W10-1903FAT.iso

Im Code-Block oben wird der Ordner D:\ISO_Files als bootfähiges (mithilfe der Dateien etfsboot.com und efisys.bin die in Windows ADK enthalten sind) Abbild im ISO-Format im Pfad D:\W10-1903FAT.iso erzeugt.

Einen startfähigen USB-Stick mit dem W10-ISO-Abbild erstellen

Das ISO-Abbild W10_<Erstell-Datum>.ISO kann mit einer vielzahl an Software-Tools auf einen USB-Stick übertragen werden. Damit kann ein Endgerät mithilfe des USB-Sticks gestartet (bootfähig) und Windows 10 mit aktuellen Patch-Level installiert werden. Der Author verwendet hier das Tool Rufus von Pete Batard.

Im Idealfall sind in Rufus nur die Auswahl des ISO-Abbilds und das Starten zum Erstellen notwendig.

.WIM-Abbild in Microsoft-Deployment-Toolkit (MDT) weiterverarbeiten

Abbilder im .WIM-Format können in MDT hervorragend dazu verwendet werden, um laufend aktualisierte Betriebssystem-Abbilder am Campus bereitzustellen. Hierbei können sowohl Microsoft Client- sowie Server-Betriebssysteme bereitgestellt werden.

Der Import sogenannter WIM-Abbilder in MDT stellt laufend aktualisierte Referenz-Images bereit.

Alternative Tools zur scriptbasierten Variante

NT Lite
MSMG Toolkit
OSD Builder
NT Lite

NTLite ist ein Computerprogramm, mit dessen Hilfe alternative Installationsmedien von Windows 7, 8, 8.1 und 10 erstellt werden können. Mit NTLite können Addons, Treiber, Sprachpakete, Silent Installer und Updates in ein Installationsmedium integriert werden.

MSMG Toolkit
  • Integrate localization packs, drivers, feature packs, add-on packs and updates.

  • Remove Windows components, simplify installation and free up drive space.

  • You can set the look and feel of Windows OS as you like with cursors, fonts, themes and skins.

  • Automate Windows setup tasks, save and deploy the Windows installation image.

=> Website des Authors

OSD Builder

OSDBuilder is a PowerShell module to help you perform Offline Servicing to a Windows Operating System Image. By using an Offline method of configuring an Operating System, it can then be imported in MDT or SCCM and used like any other OS Deployment. This includes being able to use in an Upgrade Task Sequence, which you cannot do with a Captured Image.

The main difference between OSDBuilder and other scripted methods for Servicing a Windows Image Offline is that OSDBuilder creates an answer file called a Task (think Task Sequence). Since the Task has all the information it needs to update the Windows Image, there is no interaction necessary, and as long as the content (updates) are updated regularly, the Task can be repeated as needed.

=> Website des Authors David Segura