From 172b74694b5c2030a961358489f283fc30d43009 Mon Sep 17 00:00:00 2001 From: Manuel Thalmann Date: Mon, 12 Jun 2023 19:12:21 +0200 Subject: [PATCH] Add scripts for creating an automatic windows installer iso --- .env.template | 2 + .gitignore | 2 + .vscode/launch.json | 15 ++ Autounattend.xml | 241 ++++++++++++++++++++++++++++ deploy.sh | 116 +++++++++++++ lib/choose-disk.sh | 59 +++++++ winfs/Scripts/Setup.cmd | 8 + winfs/Scripts/Setup.ps1 | 13 ++ winfs/Windows/System32/startnet.cmd | 3 + 9 files changed, 459 insertions(+) create mode 100644 .env.template create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 Autounattend.xml create mode 100755 deploy.sh create mode 100644 lib/choose-disk.sh create mode 100644 winfs/Scripts/Setup.cmd create mode 100644 winfs/Scripts/Setup.ps1 create mode 100644 winfs/Windows/System32/startnet.cmd diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..bc4bff5 --- /dev/null +++ b/.env.template @@ -0,0 +1,2 @@ +# The path to the Windows 11 ISO image +WIN11_IMAGE_PATH= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e8644cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +build/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b124709 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "bashdb", + "request": "launch", + "name": "Bash-Debug (simplest configuration)", + "program": "${file}", + "terminalKind": "integrated" + } + ] +} \ No newline at end of file diff --git a/Autounattend.xml b/Autounattend.xml new file mode 100644 index 0000000..7f06c8f --- /dev/null +++ b/Autounattend.xml @@ -0,0 +1,241 @@ + + + + + + + + en-US + + 0807:00000807 + en-US + en-US + en-US + en-US + + + + + 0 + true + + + + 1 + Primary + 300 + + + + 2 + EFI + 100 + + + + 3 + MSR + 128 + + + + 4 + Primary + true + + + + + + 1 + 1 + + NTFS + de94bba4-06d1-4d40-a16a-bfd50179d6ac + + + + 2 + 2 + + FAT32 + + + + 3 + 3 + + + + 4 + 4 + + C + NTFS + + + + OnError + + + + + 0 + 4 + + + + + + + + + Never + + true + Admin + + + + + + + false + + + + + 1 + + + + + 0807:00000807 + en-US + en-US + en-US + en-US + + + true + + + 0 + + + DerGeret + W269N-WFGWX-YVC9B-4J6C9-T83GX + + + + + + + + true</PlainText> + </Password> + <Enabled>true</Enabled> + <Username>Admin</Username> + </AutoLogon> + <OOBE> + <HideEULAPage>true</HideEULAPage> + <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> + <HideOnlineAccountScreens>true</HideOnlineAccountScreens> + <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> + <NetworkLocation>Home</NetworkLocation> + <SkipUserOOBE>true</SkipUserOOBE> + <SkipMachineOOBE>true</SkipMachineOOBE> + <ProtectYourPC>1</ProtectYourPC> + </OOBE> + <UserAccounts> + <LocalAccounts> + <LocalAccount wcm:action="add"> + <Password> + <Value></Value> + <PlainText>true</PlainText> + </Password> + <Description></Description> + <DisplayName>Admin</DisplayName> + <Group>Administrators</Group> + <Name>Admin</Name> + </LocalAccount> + </LocalAccounts> + </UserAccounts> + <RegisteredOrganization></RegisteredOrganization> + <RegisteredOwner>Admin</RegisteredOwner> + <DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet> + <FirstLogonCommands> + <SynchronousCommand wcm:action="add"> + <Description>Control Panel View</Description> + <Order>1</Order> + <CommandLine>reg add + "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ControlPanel" + /v StartupPage /t REG_DWORD /d 1 /f</CommandLine> + <RequiresUserInput>true</RequiresUserInput> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>2</Order> + <Description>Control Panel Icon Size</Description> + <RequiresUserInput>false</RequiresUserInput> + <CommandLine>reg add + "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ControlPanel" + /v AllItemsIconView /t REG_DWORD /d 0 /f</CommandLine> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>3</Order> + <RequiresUserInput>false</RequiresUserInput> + <CommandLine>cmd /C wmic useraccount where name="Admin" set + PasswordExpires=false</CommandLine> + <Description>Password Never Expires</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>4</Order> + <RequiresUserInput>false</RequiresUserInput> + <CommandLine>powershell -c "Set-ExecutionPolicy Bypass"</CommandLine> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>5</Order> + <RequiresUserInput>false</RequiresUserInput> + <CommandLine>powershell -c "Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'));"</CommandLine> + </SynchronousCommand> + </FirstLogonCommands> + <TimeZone>W. Europe Standard Time</TimeZone> + </component> + </settings> +</unattend> diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..9763382 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,116 @@ +#!/bin/bash +WIN_DISK="${WIN_DISK}"; +workingDir="$(pwd)"; +overlayDir="$(mktemp -d)"; +pushd "${BASH_SOURCE%/*}" > /dev/null; +. "./.env" > /dev/null 2>&1; +. "./lib/choose-disk.sh"; + +if [ ! -z "$WIN11_IMAGE_PATH" ] +then + WIN11_IMAGE_PATH="$(bash -c "realpath $WIN11_IMAGE_PATH")"; +fi; + +if [ ! -f "$WIN11_IMAGE_PATH" ] +then + if [ ! -f "./.env" ] + then + cp .env.template .env; + fi; + + echo "Please specify the path to the Windows 11 ISO image in your .env file located at:"; + realpath --relative-to "$workingDir" "$(realpath .env)"; +else + mountPath="/media/wininstall"; + bootPath="/media/boot"; + dataPath="/media/data"; + setupLabel="winiso"; + pwshArchive="./build/pwsh.zip"; + editionField="Edition ID"; + + cp -r winfs/* "$overlayDir"; + + if [ ! -f "$pwshArchive" ] + then + curl -L "https://github.com/PowerShell/PowerShell/releases/download/v7.3.4/PowerShell-7.3.4-win-x64.zip" -o "$pwshArchive"; + fi; + + unzip "$pwshArchive" -d "$overlayDir/PowerShell"; + + sudo mount --mkdir "$WIN11_IMAGE_PATH" "$mountPath"; + isoFile="./build/win.iso"; + mkdir -p "$(dirname "$isoFile")"; + mkwinpeimg --iso --arch amd64 --overlay "$overlayDir" --windows-dir "$mountPath" "$isoFile"; + sudo umount "$mountPath"; + + if [ ! -b "$WIN_DISK" ] + then + chooseDisk WIN_DISK; + fi; + + { + echo "o"; + + echo "n"; + echo ""; + echo ""; + echo ""; + echo "+2G"; + echo "y"; + echo "t"; + echo "c"; + echo "a"; + echo ""; + + echo "n"; + echo ""; + echo ""; + echo ""; + echo ""; + echo "y"; + echo "t"; + echo ""; + echo "7"; + + echo "w"; + } | sudo fdisk "$WIN_DISK"; + + while true + do + disks=($(bash -c "echo $WIN_DISK*")); + [ "${#disks[@]}" -ge 3 ] && break; + done; + + bootDisk="${disks[1]}"; + dataDisk="${disks[2]}"; + sudo mkfs.fat -F 32 -n "BOOT" "$bootDisk"; + sudo mkfs.ntfs -fFL "$setupLabel" "$dataDisk"; + sudo mount --mkdir "$bootDisk" "$bootPath"; + sudo mount --mkdir "$dataDisk" "$dataPath"; + sudo mount --mkdir "$isoFile" "$mountPath"; + sudo cp -r "$mountPath"/* "$bootPath"; + sudo umount "$mountPath"; + + sudo mount "$WIN11_IMAGE_PATH" "$mountPath"; + sudo cp -r "$mountPath"/* "$dataPath"; + sudo cp Autounattend.xml "$dataPath"; + sudo cp -r "$mountPath/efi" "$bootPath"; + wimFile="$dataPath/sources/install.wim"; + + while [ ! "$(echo $(wiminfo "$wimFile" 1 | grep "^$editionField" | cut -d ":" -f2))" == "Professional" ] + do + sudo wimdelete --soft "$wimFile" 1; + done; + + while wiminfo "$wimFile" 2 2>&1 > /dev/null + do + sudo wimdelete --soft "$wimFile" 2; + done; + + sudo umount "$mountPath"; + sudo umount "$bootPath"; + sudo umount "$dataPath"; + sudo rm -rf "$mountPath" "$bootPath" "$dataPath"; +fi; + +popd > /dev/null; diff --git a/lib/choose-disk.sh b/lib/choose-disk.sh new file mode 100644 index 0000000..c1e8a8d --- /dev/null +++ b/lib/choose-disk.sh @@ -0,0 +1,59 @@ +#!/bin/bash +function chooseDisk() { + local -n result="$1"; + local message="$2"; + local choice; + local disk; + local -a disks; + local i; + disks=(); + + while read disk + do + local -a diskInfo; + diskInfo=($disk); + + if [ "${diskInfo[2]}" == "TYPE" ] || [ "${diskInfo[2]}" == "disk" ] + then + disks+=("$disk"); + fi; + done < <(lsblk -do NAME,SIZE,TYPE); + + diskCount="$(expr "${#disks[@]}" - 1)"; + padding="${#diskCount}"; + + if [ "$diskCount" -gt 0 ] + then + while true + do + echo "$message"; + + for i in $(seq 0 "$(expr "$diskCount")") + do + local index; + if [ "$i" -eq 0 ] + then + index=""; + else + index="$i:"; + fi; + + printf "%$(expr "${diskCount}" + 1)s ${disks[$i]}" "$index"; + echo ""; + done; + + read -p "Your choice: " choice; + + if [ "$choice" -ge 1 ] && [ "$choice" -le "$diskCount" ] + then + disk=(${disks[$choice]}); + result="/dev/${disk[0]}"; + return; + else + >&2 echo "The specified choice \"$choice\" is invalid!"; + fi; + done; + else + >&2 echo "No valid disk found!"; + fi; +} diff --git a/winfs/Scripts/Setup.cmd b/winfs/Scripts/Setup.cmd new file mode 100644 index 0000000..d5ecd3e --- /dev/null +++ b/winfs/Scripts/Setup.cmd @@ -0,0 +1,8 @@ +@echo off +rem +rem Run PowerShell script to start Windows Setup +rem +cls +echo. +echo Starting windows Setup... +X:\PowerShell\pwsh.exe -ExecutionPolicy bypass -file "X:\Scripts\Setup.ps1" diff --git a/winfs/Scripts/Setup.ps1 b/winfs/Scripts/Setup.ps1 new file mode 100644 index 0000000..7b21c0b --- /dev/null +++ b/winfs/Scripts/Setup.ps1 @@ -0,0 +1,13 @@ +function Invoke-Diskpart { + param ( + $script + ) + + $file = New-TemporaryFile; + $null = Set-Content "$file" "$script"; + & diskpart -s "$file"; +} + +$drives = & wmic volume get "DriveLetter,Label"; +$drive = $($($drives | Select-String -Pattern "winiso") -split "\s+")[0]; +& "$drive\setup.exe"; diff --git a/winfs/Windows/System32/startnet.cmd b/winfs/Windows/System32/startnet.cmd new file mode 100644 index 0000000..dbeae2d --- /dev/null +++ b/winfs/Windows/System32/startnet.cmd @@ -0,0 +1,3 @@ +wpeinit +powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c +X:\Scripts\Setup.cmd