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).
Install-SetupDeploymentv1909.zip
3KB
Binary
Install-SetupDeploymentv1909.zip
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
1
2
# Check for elevation
3
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
4
[Security.Principal.WindowsBuiltInRole] "Administrator"))
5
{
6
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."
7
Write-Warning "Aborting script..."
8
Break
9
}
10
11
# Our Deployment Folder Structure
12
$deploy_dir = "$env:HOMEDRIVE\SetupDeployment"
13
$deploy_DismPath = "$env:HOMEDRIVE\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM"
14
$deploy_OscdImgPath = "$env:HOMEDRIVE\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg"
15
$deploy_src = "$deploy_dir\src"
16
$deploy_drivers = "$deploy_dir\src\drivers"
17
$deploy_ADKOffline = "$deploy_dir\src\ADKOffline"
18
$deploy_ADKWinPeOffline = "$deploy_dir\src\ADKWinPeOffline"
19
# $deploy_UpdatesDir = "$deploy_dir\src\Updates"
20
$deploy_logs = "$deploy_dir\logs"
21
$deploy_build = "$deploy_dir\build"
22
$deploy_mount = "$deploy_dir\build\mount"
23
$deploy_iso = "$deploy_dir\ISO"
24
$deploy_tmp = "$deploy_dir\tmp"
25
$deploy_WSIMUpdate = "$deploy_dir\src\WSIM1903Update"
26
27
if (!(Test-Path -path $deploy_dir)) {New-Item $deploy_dir -Type Directory}
28
if (!(Test-Path -path $deploy_src)) {New-Item $deploy_src -Type Directory}
29
if (!(Test-Path -path $deploy_drivers)) {New-Item $deploy_drivers -Type Directory}
30
if (!(Test-Path -path $deploy_ADKOffline)) {New-Item $deploy_ADKOffline -Type Directory}
31
if (!(Test-Path -path $deploy_ADKWinPeOffline)) {New-Item $deploy_ADKWinPeOffline -Type Directory}
32
# if (!(Test-Path -path $deploy_UpdatesDir)) {New-Item $deploy_UpdatesDir -Type Directory}
33
# if (!(Test-Path -path $deploy_UpdatesDirLCU)) {New-Item $deploy_UpdatesDirLCU -Type Directory}
34
# if (!(Test-Path -path $deploy_UpdatesDirSSU)) {New-Item $deploy_UpdatesDirSSU -Type Directory}
35
if (!(Test-Path -path $deploy_WSIMUpdate)) {New-Item $deploy_WSIMUpdate -Type Directory}
36
if (!(Test-Path -path $deploy_logs)) {New-Item $deploy_logs -Type Directory}
37
if (!(Test-Path -path $deploy_build)) {New-Item $deploy_build -Type Directory}
38
if (!(Test-Path -path $deploy_mount)) {New-Item $deploy_mount -Type Directory}
39
if (!(Test-Path -path $deploy_tmp)) {New-Item $deploy_tmp -Type Directory}
40
if (!(Test-Path -path $deploy_iso)) {New-Item $deploy_iso -Type Directory}
41
42
# 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.
43
# https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install
44
# ADK Version 1903 Url and Install Options
45
# https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install
46
$adk_url = 'https://download.microsoft.com/download/B/E/6/BE63E3A5-5D1C-43E7-9875-DFA2B301EC70/adk/adksetup.exe'
47
$adk_file = 'adksetup.exe'
48
$adk_features = '*' # https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-8.1-and-8/dn621910(v=win.10)
49
$adk_install_log = "$deploy_logs\adksetup.log"
50
$adk_reg_key = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{fb450356-9879-4b2e-8dc9-282709286661}"
51
$adk_winPeUrl = "https://download.microsoft.com/download/E/F/A/EFA17CF0-7140-4E92-AC0A-D89366EBD79E/adkwinpeaddons/adkwinpesetup.exe"
52
$adk_winPeFile = "adkwinpesetup.exe"
53
$mdt_reg_key = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{2E6CD7B9-9D00-4B04-882F-E6971BC9A763}"
54
$mdt_fileURL = "https://download.microsoft.com/download/3/3/9/339BE62D-B4B8-4956-B58D-73C4685FC492/MicrosoftDeploymentToolkit_x64.msi"
55
$mdt_fileName = "MicrosoftDeploymentToolkit_x64.msi"
56
$adk_WinPeInstall_log= "C:\SetupDeployment\logs\ADKWinPEInstallation.log"
57
58
Add-Type -AssemblyName "system.io.compression.filesystem"
59
60
Start-Sleep -Seconds 2
61
62
if(-not (Test-Path -Path $adk_reg_key))
63
{
64
# Offline Download ADK
65
# $downADK = (New-Object System.Net.WebClient).DownloadFile($adk_url,"$deploy_src\$adk_file")
66
# Invoke-WebRequest -uri $adk_url -OutFile $deploy_src\$adk_file -PassThru -Verbose
67
Write-Host "`nDownload von $adk_url erfolgt ..." -ForeGroundColor Green
68
Start-BitsTransfer -Source $adk_url -Destination "$deploy_src\$adk_file"
69
Write-Host "`nInstallation von $deploy_src\$adk_file erfolgt ..." -ForeGroundColor Green
70
Start-Process -FilePath "$deploy_src\$adk_file" -ArgumentList "/quiet /layout $deploy_ADKOffline /log `"$adk_install_log`"" -wait -Verbose
71
[System.GC]::Collect()
72
73
# Offline Download WinPE-ADK
74
# $downADKPE = (New-Object System.Net.WebClient).DownloadFile($adk_winPeUrl,"$deploy_src\$adk_winPeFile")
75
# Invoke-WebRequest -UseBasicParsing -uri $adk_winPeUrl -OutFile $deploy_src\$adk_winPeFile -PassThru -Verbose
76
Write-Host "`nDownload von $adk_winPeUrl erfolgt ..." -ForeGroundColor Green
77
Start-BitsTransfer -Source $adk_winPeUrl -Destination "$deploy_src\$adk_winPeFile"
78
Write-Host "`nInstallation von $deploy_src\$adk_winPeFile erfolgt ..." -ForeGroundColor Green
79
Start-Process -FilePath "$deploy_src\$adk_winPeFile" -ArgumentList "/quiet /layout $deploy_ADKWinPeOffline /log `"$adk_WinPeInstall_log`"" -wait -Verbose
80
[System.GC]::Collect()
81
82
# Install ADK Offline-Repository
83
Write-Host "`nInstallation von Windows ADK erfolgt ..." -ForeGroundColor Green
84
Start-Process -FilePath "$deploy_ADKOffline\$adk_file" -ArgumentList "/quiet /norestart /log `"$adk_install_log`"" -wait -PassThru -Verbose
85
86
# Install WinPE Offline-Repository
87
Write-Host "`nInstallation von Windows PE erfolgt ..." -ForeGroundColor Green
88
Start-Process -FilePath "$deploy_ADKWinPeOffline\$adk_winPeFile" -ArgumentList "/quiet /norestart /log `"$adk_WinPeInstall_log`"" -wait -PassThru -Verbose
89
90
# Download and Install the Windows System Image Manager (WSIM) 1903 update
91
$WSIMUpdateURL = "https://download.microsoft.com/download/4/0/F/40FD29FC-0E6D-46D0-8F7E-B033110D07D5/WSIM1903.zip"
92
Write-Host "`nDownload von Windows System Image Manager (WSIM) 1903 update erfolgt ..." -ForeGroundColor Green
93
Start-BitsTransfer -Source $WSIMUpdateURL -Destination "$deploy_src\WSIM1903Update\WSIM1903.zip"
94
[io.compression.zipfile]::ExtractToDirectory("$deploy_src\WSIM1903Update\WSIM1903.zip", "$deploy_src/WSIM1903Update/") | Out-Null
95
Write-Host "`nInstallation von Windows System Image Manager (WSIM) 1903 update erfolgt ..." -ForeGroundColor Green
96
Start-Process -FilePath "cmd.exe" -ArgumentList "/c $deploy_src\WSIM1903Update\UpdateWSIM.bat" -WindowStyle Hidden -WorkingDirectory "$deploy_src/WSIM1903Update/" -PassThru -Wait
97
}
98
99
# Install MDT 8456
100
if(-not (Test-Path -Path $mdt_reg_key)) {
101
Write-Host "`nDownload von $mdt_fileName erfolgt ..." -ForeGroundColor Green
102
Start-BitsTransfer -Source $mdt_fileURL -Destination "$deploy_src\$mdt_fileName"
103
# Invoke-WebRequest -UseBasicParsing -uri $mdt_fileURL -OutFile $deploy_src\$mdt_fileName -PassThru -Verbose
104
Write-Host "`nInstallation von $deploy_src\$mdt_fileName erfolgt ..." -ForeGroundColor Green
105
Start-Process -FilePath "$deploy_src\$mdt_fileName" -ArgumentList "/passive /norestart /L*V `"$deploy_logs\mdt_install.log`"" -wait -PassThru -Verbose
106
107
# Download and extract Sysinternals
108
$SysinternalsURL = "https://download.sysinternals.com/files/SysinternalsSuite.zip"
109
Write-Host "`nDownload von $SysinternalsURL erfolgt ..." -ForeGroundColor Green
110
Start-BitsTransfer -Source $SysinternalsURL -Destination "$deploy_src\Sysinternals.zip"
111
Write-Host "`nEntpacken von $deploy_src\Sysinternals.zip erfolgt ..." -ForeGroundColor Green
112
[io.compression.zipfile]::ExtractToDirectory("$deploy_src\Sysinternals.zip", "$deploy_src/Sysinternals")
113
114
}
115
116
# Check if SSU OR LCU exists else get it
117
# if ((Get-ChildItem $deploy_UpdatesDirSSU -Filter *.msu | measure).Count -eq 0 `
118
# -OR (Get-ChildItem $deploy_UpdatesDirLCU -Filter *.msu | measure).Count -eq 0) {
119
# Import DISM modules from Windows 10 ADK
120
Import-Module $deploy_DismPath -Force
121
122
# Global Variables
123
& setx /M "PATH" "$ENV:PATH;$deploy_DismPath"
124
125
# Get Latest Updates Windows 10 Version 1903
126
# https://docs.stealthpuppy.com/docs/latestupdate/usage/get-stack
127
# Install-PackageProvider -Name NuGet -Force -Confirm:$false
128
# Install-Module -Name LatestUpdate -Force -Confirm:$false -AllowClobber
129
# Import-Module -Name LatestUpdate -Force -PassThru
130
131
# SSU
132
# Get-LatestServicingStackUpdate -OperatingSystem Windows10 -Version 1903 | `
133
# Where-Object { $_.Architecture -eq "x64" -and $_.Note -notmatch "Server"} | `
134
# Save-LatestUpdate -Path $deploy_UpdatesDirSSU
135
136
# LCU
137
# Get-LatestCumulativeUpdate -OperatingSystem Windows10 -Version 1903 | `
138
# Where-Object { $_.Architecture -eq "x64" -and $_.Note -notmatch "Server"} | `
139
# Save-LatestUpdate -Path $deploy_UpdatesDirLCU
140
141
# Unblock Files
142
# Get-ChildItem -Path $deploy_UpdatesDir -Filter *.msu -Recurse -Force | Unblock-File
143
# }
144
145
# Fix NTFS-permission
146
& icacls $deploy_dir /c /t /q /reset | Out-Null
147
148
# Fix MDT DeploymentShare permissions
149
# https://deploymentresearch.com/fixing-mdt-2013-update-1-deployment-share-permissions-using-powershell/
150
151
# ------------------------------------------------------------------------------------------
Copied!

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.
Create-W10RefImagev1909.zip
5KB
Binary
Create-W10RefImagev1909.zip

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

Folgende Änderungen sollten Sie vor dem ersten Aufruf ggf. vornehmen:
1
$ISO = [System.IO.Path]::Combine("$RootDir\ISO","<Name der ISO-Datei.iso>")
Copied!
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
1
<#
2
.SYNOPSIS
3
Dieses Script erstellt eine aktuell gepatchte ISO-Installations- und WIM-Datei von Windows 10 1909.
4
Führe das Script unter privilegierten Benutzerrechten (Administrator) aus um einen einwandfreien
5
Betrieb der Funktionen zu gewährleisten!
6
.DESCRIPTION
7
Für die schnelle Bereitstellung von Windows in Campus-Umgebungen sind aktuell gepatchte
8
ISO- und WIM-Dateien eine wesentliche Unterstützung. Die Weiterverarbeitung von WIM-Dateien sind
9
in MDT oder SCM-Tools von Microsoft direkt zu importieren.
10
Die ISO-Datei kann mit Tools wie Rufus (https://rufus.ie/) auf einen USB-Stick übertragen werden.
11
.PARAMETER <Parameter_Name>
12
No parameter input required.
13
.INPUTS
14
None
15
.OUTPUTS
16
WIM-file stored in C:\SetupDeployment\build\REFW10-001.wim
17
ISO-file stored in C:\SetupDeployment\build\<W10_<DATE>.ISO
18
.NOTES
19
Version: 2.0
20
Author: Alexander Scharmer
21
Creation Date: 29.12.2019
22
Purpose/Change:
23
Note #1 : To service a newer version of WinPE than the OS you are servicing from, for example service Windows 10 v1709
24
from a Windows Server 2019 server, you need a newer DISM version.
25
Solution, simply install the latest Windows ADK 10, and use DISM from that version
26
Note #2 : If your Windows OS already have a newer version of dism, uncomment the below line, and comment out line 11 and 12
27
$DISMFile = 'dism.exe'
28
29
.EXAMPLE
30
PS> . \Create-W10RefImagev1909.ps1
31
#>
32
Clear-Host
33
Set-ExecutionPolicy Bypass -Scope Process -Force
34
## Release handles on Registry Hive and free up memory
35
[System.GC]::Collect()
36
37
# Check for elevation
38
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
39
[Security.Principal.WindowsBuiltInRole] "Administrator"))
40
{
41
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."
42
Write-Warning "Aborting script..."
43
Break
44
Pause
45
}
46
47
write-host "`nOffline-Patching Window 10 Image v2.0 - by Alexander Scharmer.`n" -ForegroundColor Yellow -BackgroundColor Black
48
49
$DISMFile = 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe'
50
If (!(Test-Path $DISMFile))
51
{
52
$scriptName = $MyInvocation.MyCommand.Name
53
Write-Warning "DISM.exe bzw. Windows ADK nicht gefunden! Abbruch des Scripts $scriptName erfolgt!"
54
[console]::beep(1000,500)
55
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
56
Break
57
}
58
else { Write-Host "`nDISM.exe in Windows ADK gefunden! Das Script wird fortgesetzt ..." -ForegroundColor Green }
59
60
$RootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path)
61
$ParentDir = [System.IO.Path]::GetDirectoryName($RootDir)
62
63
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"
64
65
try{
66
<#
67
In diesem Block der Code, der eine Exception auslösen könnte
68
#>
69
if (!(Get-Module "OSDSUS"))
70
{
71
write-host "Modul OSDSUS nicht geladen ... versuche es nachzuladen ..." -ForegroundColor DarkGreen
72
Import-Module OSDSUS -Force
73
74
}
75
76
if (!(Get-Module "OSDUpdate"))
77
{
78
write-host "Modul OSDUpdate nicht geladen ... versuche es nachzuladen ..." -ForegroundColor DarkGreen
79
Import-Module OSDUpdate -Force
80
81
}
82
83
# $error[0].Exception.GetType().FullName
84
}
85
86
catch [System.IO.FileNotFoundException]
87
{
88
<# Modul OSDSUS oder OSDUpdate wurde nicht gefunden weil nicht installiert #>
89
write-host "Modul OSDSUS bzw. OSDUpdate nicht installiert ... versuche es aus dem Internet zu laden und zu installieren ..." -ForegroundColor DarkGreen
90
Install-PackageProvider -Name NuGet -Force -Scope AllUsers -ForceBootstrap
91
Install-Module OSDSUS -Scope AllUsers -Force -AllowClobber -SkipPublisherCheck -Confirm:$false Update-OSDSUS
92
Import-Module OSDSUS -Force Install-Module OSDUpdate -Scope AllUsers -Force -AllowClobber -SkipPublisherCheck -Confirm:$false Import-Module OSDUpdate -Force
93
}
94
95
catch {
96
<#
97
Hier findet die Fehlerbehandlung statt, z.B. das Schreiben eines Logs
98
Der letzte aufgezeichnete Fehler ist hier über die Variable $_ abrufbar,
99
einzelne Eigenschaften daher nach diesem Muster: $_.Exception.Message
100
#>
101
"`n==> Exception Handling durch nicht-terminierenden Fehler"
102
}
103
104
finally{
105
<#
106
Jede Anweisung in diesem Block wird immer ausgeführt, egal ob ein
107
Fehler aufgetreten ist oder nicht. Dieser Block ist optional.
108
#>
109
write-host "`n `nFinally-Block-OSD-Module: Erfolgreich Verarbeitung." -ForegroundColor Green
110
if (Get-Module "OSDSUS") { Write-Host "Modul OSDSUS erfolgreich geladen." -ForegroundColor Yellow}
111
if (Get-Module "OSDUpdate") { Write-Host "Modul OSDUpdate erfolgreich geladen." -ForegroundColor Yellow}
112
113
# $error[0].Exception.GetType().FullName
114
$error.Clear()
115
}
116
# 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
117
118
#Auxiliary
119
$ISO = [System.IO.Path]::Combine("$RootDir\ISO","SW_DVD9_Win_Pro_10_1909_64BIT_German_Pro_Ent_EDU_N_MLF_X22-17400.ISO")
120
$SSU = Get-Content -Path ([System.IO.Path]::Combine("$env:SystemDrive\SetupDeployment\Updates","Windows 10","1909","SSU","LatestFile.txt"))
121
$LCU = Get-Content -Path ([System.IO.Path]::Combine("$env:SystemDrive\SetupDeployment\Updates","Windows 10","1909","LCU","LatestFile.txt"))
122
$MountFolder = [System.IO.Path]::Combine($RootDir,'build\Mount')
123
$RefImageFolder = [System.IO.Path]::Combine($RootDir,'build')
124
$TmpImage = [System.IO.Path]::Combine("$RootDir\tmp",'tmp_install.wim')
125
$RefImage = [System.IO.Path]::Combine($RefImageFolder,'REFW10-001.wim')
126
127
# Verify that the ISO and CU files existnote
128
if (!(Test-Path -path $ISO))
129
{
130
Write-Warning "`nINFO: Could not find Windows 10 ISO $ISO. Wähle eine gültige ISO-Datei im Dialogfeld aus ..."
131
Write-Host "INFO: Im Regelfall sucht das Script eine Windows-10-ISO-Datei im Pfad $ISO" -ForegroundColor Green
132
Add-Type -AssemblyName System.Windows.Forms # https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.openfiledialog?redirectedfrom=MSDN&view=netframework-4.8
133
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
134
Title = "Wähle eine gültige Windows-10-ISO-Datei aus ..."
135
InitialDirectory = [System.IO.Directory]::GetCurrentDirectory();
136
Multiselect = $false
137
Filter = "Image-Dateien (*.iso)|*.iso";
138
CheckPathExists = 'True';
139
}
140
[void]$FileBrowser.ShowDialog()
141
$ISO = $FileBrowser.FileName
142
}
143
if (!(Test-Path -path $SSU))
144
{
145
Write-Warning "Could not find servicing stack Update for Windows 10. Try to download ..."
146
fGetUpdates -arch x64 -SLCU SSU -BuildNummer 1909
147
148
}
149
if (!(Test-Path -path $LCU))
150
{
151
Write-Warning "Could not find Cumulative Update for Windows 10. Try to download ..."
152
fGetUpdates -arch x64 -SLCU LCU -BuildNummer 1909
153
}
154
155
# Mount the Windows 10 ISO
156
Write-Host "`nTry Mounting ISO $ISO ..." -ForegroundColor Green
157
Mount-DiskImage -ImagePath $ISO | Out-Null
158
159
$ISOImage = Get-DiskImage -ImagePath $ISO | Get-Volume
160
$ISODrive = [string]$ISOImage.DriveLetter+":"
161
$ImageName = (Get-WindowsImage -ImagePath "$ISODrive\Sources\install.wim" | ? { $_.imageindex -eq '1' }).ImageName.toString()
162
163
# Export the Windows 10 Standard index 1 to a new WIM
164
Write-Host "`nExporting image $ImageName from $ISODrive\Sources\install.wim ..." -ForegroundColor Green
165
Export-WindowsImage -SourceImagePath "$ISODrive\Sources\install.wim" -SourceIndex 1 -DestinationImagePath $TmpImage
166
167
# Mount the image
168
Write-Host "`nMounting WIM-File $TmpImage ..." -ForegroundColor Green
169
Mount-WindowsImage -ImagePath $TmpImage -Index 1 -Path $MountFolder
170
171
# Add the latest SSU to the Windows 10 Standard image
172
Write-Host "`nInstalling SSU $SSU ..." -ForegroundColor Green
173
Add-WindowsPackage -PackagePath $SSU -Path $MountFolder
174
175
# Add the latest CU (LCU) to the Windows 10 Standard image
176
Write-Host "Installing LCU (bitte um Geduld, dauert etwas) $LCU ..." -ForegroundColor Green
177
Add-WindowsPackage -PackagePath $LCU -Path $MountFolder
178
179
# Cleanup the image BEFORE installing .NET to prevent errors
180
# 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.
181
# https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/clean-up-the-winsxs-folder#span-iddismexespanspan-iddismexespandismexe
182
Write-Host "`nCleaning up Image $MountFolder BEFORE installing .NET to prevent errors ..." -ForegroundColor Green
183
184
& $DISMFile /Image:$MountFolder /Cleanup-Image /StartComponentCleanup /ResetBase
185
186
# Add .NET Framework 3.5.1 to the Windows 10 Standard image
187
Write-Host "`nInstalling .Net Framework 3.5. to Offline-Image $MountFolder ..." -ForegroundColor Green
188
Add-WindowsCapability -Name NetFx3~~~~ –Source $ISODrive\sources\sxs\ -Path $MountFolder | Out-Null
189
190
# Re-apply latest CU (LCU) because of .NET changes
191
Write-Host "`nErneutes Applizieren von LCU - aufgrund von .NET 2.0-3.5-Package-Installation - erforderlich ..." -ForegroundColor Green
192
Add-WindowsPackage -PackagePath $LCU -Path $MountFolder
193
194
# Dismount the Windows 10 Standard image
195
Write-Host "`nDismounting WIM $MountFolder ..." -ForegroundColor Green
196
DisMount-WindowsImage -Path $MountFolder -Save
197
198
# Export the Windows 10 index to a new WIM (the export operation reduces the WIM size with about 100 MB or so)
199
# Optimize final image - https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/oem-deployment-of-windows-10-for-desktop-editions
200
# Remove unused packages from your image, by exporting a copy of it.
201
Write-Host "`nExporting the WIM-File $TmpImage to $RefImage ..." -ForegroundColor Green
202
Export-WindowsImage -SourceImagePath $TmpImage -SourceIndex "1" -DestinationImagePath $RefImage
203
204
# Remove the uneeded temporary WIM-File.
205
Write-Host "`nDelete temporary WIM $TmpImage ..." -ForegroundColor Green
206
if (Test-Path -path $TmpImage) {Remove-Item -Path $TmpImage -Force}
207
208
# Copy REF-IMG to Media for ISO-Build
209
Write-Host "`nCopy REF-IMG to Media for ISO-Build ..." -ForegroundColor Green
210
if (!(Test-Path -path "$RefImageFolder\media")) {New-Item -path "$RefImageFolder\media" -ItemType Directory}
211
Copy-Item -Path "$ISODrive\*" -Destination "$RefImageFolder\media" -Force -Recurse
212
Copy-Item -Path $RefImage -Destination "$RefImageFolder\Media\sources\install.wim" -Force -Filter "*.wim" -Confirm:$false
213
214
# Create Windows ISO
215
$ISOFile = "$RefImageFolder" + "\" + ([string]"W10_$(get-date -Format d)").Replace(".","_") + ".ISO"
216
write-Host "`nKonstruiere Dateiname für ISO-Datei $ISOFile" -ForegroundColor Green
217
$ToolsPath = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg"
218
$BootData='2#p0,e,b"{0}"#pEF,e,b"{1}"' -f "$ToolsPath\etfsboot.com","$ToolsPath\efisys_noprompt.bin"
219
$Proc = Start-Process -FilePath "$ToolsPath\oscdimg.exe" -ArgumentList @("-bootdata:$BootData",'-u2','-udfver102',"$RefImageFolder\media","$ISOFile") -PassThru -Wait
220
if($Proc.ExitCode -ne 0)
221
{
222
Throw "Failed to generate ISO with exitcode $($Proc.ExitCode)"
223
}
224
else {
225
Write-Host "`nMedia $ISOFile creation has been successfully done!" -ForegroundColor Yellow -BackgroundColor Black
226
}
227
228
# Dismount the Windows 10 ISO
229
Write-Host "Dismounting ISO $ISO und finalisiere Script ..." -ForegroundColor Green
230
Dismount-DiskImage -ImagePath $ISO | Out-Null
231
Write-Host "Ende. Have a nice Day!"
232
# END OF SCRIPT
Copied!

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:
1
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
Copied!
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
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.
    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.
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.
Last modified 1yr ago