The Idea creation of the image from scratch is that you can adapt your process to any new windows iso that you might have, which gives you flexibility with updating your image set.
- Windows box with VirtualBox on it
- Windows Assessment and Deployment Kit:
choco install windows-adk-all
- Clone the wimaging repo:
git clone https://github.com/kireevco/wimaging.git - Copy contents of your windows DVD to
./sources/server-<version>/ - Configure wimaging (
./inc/Config.ps1) - Create boot.wim with injected storage / network drivers and foreman toolset
- Create install.wim with injected storage / network drivers and foreman toolset
- Configure http / CIFS share
- Configure Foreman
- Provision a Windows host
Rename .\inc\Config.ps1.sample .\inc\Config.ps1 and set some parameters before doing maintenance of your image
- $os: server-2008r2 or windows-pe-x64 (x86) or server-2012r2. Very important to use provided options, as the directory structure uses that pattern. See examples in Config.ps1
- $edition: standard / enterprise / datacenter
- $boot=$false. Use $true if servicing boot.wim
- $wsus_offline_dir: directory where locally downloaded windows updates reside (for ex.:
$wsus_offline_dir = "c:\wsusoffline\client") - $system_reserved_mount: Letter for mounting a "System reserved" partition of your vhd (pick any drive letter that is not used)
- $c_drive_mount: Letter for mounting "C: Drive" partition of your vhd (pick any drive letter that is not used)
File is used during actual installation and is copied into the image as a part of the minimal toolset
Rename .\tools\install.root\Windows\Setup\Scripts\config.cmd.sample to config.cmd and set parameters:
- logfile
- deployment
- username
- password
- foremanserver
- dotnetsetupexe: DotNet4.0 installer path
We will need to share "install" directory via Windows Network Share / CIFS and (\\wimagingHost\\install) and expose it via http. Let's create some aliases on a webserver for our http endpoints (required for media concept in Foreman):
- Point http://wimagingHost/install/server-2012r2x64.standard to
.\install\server-2012r2 - Point http://wimagingHost/install/server-2012r2x64.enterprise to
.\install\server-2012r2 - Point http://wimagingHost/install/server-2008r2x64.standard to
.\install\server-2008r2 - Point http://wimagingHost/install/server-2008r2x64.enterprise to
.\install\server-2008r2
- Copy your drivers to
.\install\drivers\ - Copy
c:\Program Files (x86)\Windows Kits\8.0\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\en-us\winpe.wimto.\images\wimdows-pe-x64\sources\boot.wim - Edit
.\inc\Config.ps1:$os = "windows-pe-x64"and$boot = $true - Edit
.\tools\boot.root\Windows\System32\startnet.cmd.samplewith your data and rename it tostartnet.cmd - Run
.\Update-All.ps1that will inject drivers, tools and additional Windows PE Features and push it to install directory. Your boot.wim will be available via the nework share \deployment.domain.com\win_install\windows-pe-x64\sources\boot.wim
This will copy required files from your Windows ISO file.
.\Init-FromSource.ps1- Use WSUS Offline to save updates for Windows Server 2012 R2 Locally
- Make sure to edit .\inc\Config.ps1 to set
$wsus_offline_dir = "c:\<path_to_wsusoffline>\client"
We will create a wim file for a specific wim index and work on it to add all required items.
# Initializing a working .wim file
.\Init-WorkWim.ps1
# Adding required items
.\Update-All.ps1Pusing our image to .\install\ directory, where it will be available to foreman
.\Push-Wim.ps1Initialize boot tools (wimboot, etc)
.\Init-PxeTools.ps1In case you are working with 2008 R2 - the process is a little bit different (because we can't inject certain updates to windows server 2008 image.
- Install virtualbox
choco install virtualbox - Download Windows VLK iso for Windows Server Enterprise 2008 R2 with SP1 (Make sure you download MAK, if you don't have a KMS server)
- Mount the distribution iso (using Daemon Tools, for example) and copy contents to \images\server-2008r2\install, unmount the iso from Daemon Tools
- Create a virtual machine in VirtualBox. Create a disk with a specific name: ., use vhd format. For example server-2008r2.enterprise.vhd
- Install OS using iso on the VM (Point your virtual DVD to the iso you downloaded, Don't worry about partitioning right now - everything will be wiped)
- Shutdown the VM, add tools using Add-Tools.ps1
- Start the VM back and run in it c:\wimaging\image\10_start.cmd - this will install updates and sysprep your box. It will shut it down is well.
It is very easy to image windows when it's in a VirtualBox. Just because of Windows native support of vhd files!
- Shut down your VM (should be shut down after your sysprep)
- Run
.\CaptureWim.ps1. By default it will initialize (create) original install.wim in a.\images\<os>\workdirectory and merge the captured image into it. - Run
.\Add-Tools.ps1to add the contents of.\tools\install.root. Feel free to add your tools that you would like to be in your image. - Run
.\Push-WimInstall.ps1. It will push your working copy of install.wim to your install location (by default -.\install\<os>\sources\install.wimYour install.wim will be available via the nework share\\deployment.domain.com\win_install\<os>\sources\install.wim
- Add wimboot bootloader to your
/tftpd/boot/on your PXE server. - Add installation media (Example)
- Add OS (http://foremanHost/operatingsystems) (Example)
- Create
./sources/server-<version>/ - Change
.\inc\Params.ps1: Add new os handlerto# Directory where the updates are locatedsection. Make sure to verify WIM image names / indexes (Windows Server 2012 R2 SERVERENTERPRISE. Run.\Get-WimInfo.ps1) - Follow the Flow
Here is an example configuration for Windows Server 2012 R2 Standard:
This templates will be triggered by wimaging at the end of provisioning on the machine that we will be building. http:///config_templates -> New Template
- Provisioning Template
- Name: WAIK default
- Template Editor:
<%# kind: finish name: WAIK default # Script is downloaded by c:\wimaging\deploy\10_init.cmd # Parameters are expected to be set in Foreman (globally or per group/host) params: - netUser: username # username to access CIFS share - netPassword: P@ssword # password to access to CIFS share - netDriveLetter: Z # drive to mount CIFS while provisioning - deploymentShare: 192.168.10.5 # Hostname/ip address of your CIFS deployment share - foremanServer: 192.168.10.7 # Hostname/ip for your foreman server - localAdminUser: Administrator # Username that will be used for local admin - localAdminPassword: AdminPassword123 # Password that will be used for local admin - windowsLicenseKey: ABCDE-ABCDE-ABCDE-ABCDE-ABCDE # Valid Windows license key - windowsLicenseOwner: Company, INC # Legal owner of the Windows license key %> set netUser=<@= @host.params['netUser'] %> set netPassword=<@= @host.params['netPassword'] %> set netDriveLetter=<@= @host.params['netDriveLetter'] %> set deploymentShare=<@= @host.params['deploymentShare'] %> set foremanServer=<@= @host.params['foremanServer'] %> set tempAdminUser=tempAdmin set tempAdminPassword=tempAdminPassword net user /add %tempAdminUser% %tempAdminPassword% net localgroup Administrators %tempAdminUser% /add <% if @host.params['localAdminUser'] -%> echo Creating local user: <%= @host.params['localAdminUser'] %> net user /add /logonpasswordchg:yes <%= @host.params['localAdminUser'] %> <%= @host.params['localAdminPassword'] %> net localgroup Administrators <%= @host.params['localAdminUser'] %> /add <% end -%> <% if @host.pxe_build? %> @echo on :: Image Tools Config cd %deployRoot%\ ::call config.cmd (echo Updating time) (sc config w32time start= auto) sc start w32time :ntp_testip ipconfig /renew ping -n 1 %deploymentShare% > NUL if %errorlevel% == 0 goto ntp_testip_ok REM wait 3 sec. and try it again ping -n 3 127.0.0.1 >nul goto ntp_testip :ntp_testip_ok echo Ping to %deploymentShare% OK! w32tm /resync w32tm /resync w32tm /resync echo Adding Credentials psexec.exe -accepteula -h -u %tempAdminUser% -p %tempAdminPassword% cmd /c "cmdkey /add:%deploymentShare% /user:%netUser% /pass:%netPassword%" echo Install: Start Chocolatey setup cmd /c powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" echo Install: Start Setting Chocolatey environment variables SETX /M PATH "%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" SETX /M ChocolateyInstall "%ALLUSERSPROFILE%\chocolatey" psexec.exe -accepteula -h -u %tempAdminUser% -p %tempAdminPassword% cmd /c "SETX PATH '%PATH%;%ALLUSERSPROFILE%\chocolatey\bin'" psexec.exe -accepteula -h -u %tempAdminUser% -p %tempAdminPassword% cmd /c "SETX ChocolateyInstall '%ALLUSERSPROFILE%\chocolatey'" echo Install powershell 3 call %ALLUSERSPROFILE%\Chocolatey\bin\choco install powershell -y echo Install puppet agent via chocolatey call %ALLUSERSPROFILE%\Chocolatey\bin\choco install puppet -installargs "PUPPET_MASTER_SERVER=<%= @host.puppetmaster %>PUPPET_AGENT_ENVIRONMENT='<%= @host.environment %>' PUPPET_AGENT_STARTUP_MODE=Manual" -y echo Running initial puppet agent for %tempAdminUser% user cd "C:\Program Files (x86)\Puppet Labs\Puppet\bin" psexec.exe -accepteula -h -u %tempAdminUser% -p %tempAdminPassword% cmd /c "puppet.bat agent --test --debug" echo Starting Puppet Agent Service (sc start puppet) && echo Service Start OK echo Setup puppet to run on system reboot sc config puppet start= auto echo Creating rebootOne task psexec.exe -accepteula -h -u %tempAdminUser% -p %tempAdminPassword% cmd /c "schtasks /create /tn rebootOne /tr %deployRoot%\50_rebootOne.cmd > %logRoot%\50_rebootOne.log /sc onstart /ru %tempAdminUser% /rp %tempAdminPassword%" :: You can join your machine to the domain right here > :: < You can join your machine to the domain right here echo Removing %tempAdminUser% net user /delete %tempAdminUser% echo The rest of setup will continue after reboot (50_rebootOne.cmd) shutdown -r <% end -%> - Type
- Snippet: no
- Type: finish
- Association
- Applicable OS: Windwows 2012 R2 -
This template will allow us to boot into a wimboot bootloader that will load our ```boot.wim``.
http:///config_templates -> New Template
- Provisioning Template
- Name: WAIK default PXELinux
- Template Editor:
<%# kind: PXELinux name: WAIK default PXELinux %> DEFAULT menu LABEL menu COM32 linux.c32 <%= @kernel %> APPEND initrdfile=<%= @initrd %>,<%= @host.operatingsystem.bootfile(@host.arch,:bcd) %>,<%= @host.operatingsystem.bootfile(@host.arch,:bootsdi) %>,<%= @host.operatingsystem.bootfile(@host.arch,:bootwim) %> - Type
- Snippet: no
- Type: PXELinux
- Association
- Applicable OS: Windwows 2012 R2
This template will be used when we start our setup. http:///config_templates -> New Template
- Provisioning Template
- Name: Windows unattend.xml
- Template Editor:
<%# kind: provision name: WAIK default unattend.xml # Parameters are expected to be set in Foreman (globally or per group/host) params: - netUser: username # username to access CIFS share - netPassword: P@ssword # password to access to CIFS share - netDriveLetter: Z # drive to mount CIFS while provisioning - deploymentShare: 192.168.10.5 # Hostname/ip address of your CIFS deployment share - foremanServer: 192.168.10.7 # Hostname/ip for your foreman server - localAdminUser: Administrator # Username that will be used for local admin - localAdminPassword: AdminPassword123 # Password that will be used for local admin - windowsLicenseKey: ABCDE-ABCDE-ABCDE-ABCDE-ABCDE # Valid Windows license key - windowsLicenseOwner: Company, INC # Legal owner of the Windows license key %> <?xml version="1.0" encoding="utf-8"?> <unattend xmlns="urn:schemas-microsoft-com:unattend"> <servicing></servicing> <settings pass="offlineServicing"> <component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <EnableLUA>false</EnableLUA> </component> </settings> <settings pass="windowsPE"> <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <EnableFirewall>false</EnableFirewall> <EnableNetwork>true</EnableNetwork> <Restart>Restart</Restart> <%= @host.diskLayout %> <ImageInstall> <OSImage> <InstallFrom> <MetaData wcm:action="add"> <Key>/IMAGE/NAME</Key> <% if @host.medium.path == "http://"+@host.params['deploymentShare'] + "/install/server-2008r2x64.standard/" -%> <Value>Windows Server 2008 R2 SERVERSTANDARD</Value> <% elsif @host.medium.path == "http://"+@host.params['deploymentShare'] + "/install/server-2008r2x64.enterprise" -%> <Value>Windows Server 2008 R2 SERVERENTERPRISE</Value> <% elsif @host.medium.path == "http://"+@host.params['deploymentShare'] + "/install/server-2012r2x64.standard" -%> <Value>Windows Server 2012 R2 SERVERSTANDARD</Value> <% end -%> </MetaData> </InstallFrom> <InstallToAvailablePartition>true</InstallToAvailablePartition> <WillShowUI>OnError</WillShowUI> </OSImage> </ImageInstall> <UserData> <AcceptEula>true</AcceptEula> </UserData> </component> <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SetupUILanguage> <UILanguage>en-US</UILanguage> </SetupUILanguage> <InputLocale>en-US</InputLocale> <SystemLocale>en-US</SystemLocale> <UILanguageFallback>en-US</UILanguageFallback> <UILanguage>en-US</UILanguage> <UserLocale>en-US</UserLocale> </component> </settings> <settings pass="oobeSystem"> <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <InputLocale>en-US</InputLocale> <SystemLocale>en-US</SystemLocale> <UILanguage>en-US</UILanguage> <UILanguageFallback>en-US</UILanguageFallback> <UserLocale>en-US</UserLocale> </component> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <UserAccounts> <AdministratorPassword> <%= Base64.encode64(Encoding::Converter.new("UTF-8", "UTF-16LE",:undef => nil).convert(@host.params['localAdminPassword']+"AdministratorPassword")).delete!("\n").chomp %> <PlainText>false</PlainText> </AdministratorPassword> </UserAccounts> <TimeZone>Pacific Standard Time</TimeZone> <% if @host.params['windowsLicenseOwner'] -%> <RegisteredOrganization><%= @host.params['windowsLicenseOwner'] %></RegisteredOrganization> <RegisteredOwner><%= @host.params['windowsLicenseOwner'] %></RegisteredOwner> <% end -%> <OOBE> <HideEULAPage>true</HideEULAPage> <NetworkLocation>Work</NetworkLocation> <ProtectYourPC>1</ProtectYourPC> <SkipUserOOBE>true</SkipUserOOBE> <SkipMachineOOBE>true</SkipMachineOOBE> </OOBE> <ShowWindowsLive>false</ShowWindowsLive> <% if @host.params['windowsLicenseKey'] -%> <ProductKey><%= @host.params['windowsLicenseKey'].to_s %></ProductKey> <% end -%> </component> </settings> <settings pass="specialize"> <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <InputLocale>en-US</InputLocale> <SystemLocale>en-US</SystemLocale> <UILanguage>en-US</UILanguage> <UILanguageFallback>en-US</UILanguageFallback> <UserLocale>en-US</UserLocale> </component> <component name="Microsoft-Windows-IE-ESC" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <IEHardenAdmin>false</IEHardenAdmin> </component> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ComputerName><%= @host.shortname %></ComputerName> <% if @host.params['windowsLicenseOwner'] -%> <RegisteredOrganization><%= @host.params['windowsLicenseOwner'] %></RegisteredOrganization> <RegisteredOwner><%= @host.params['windowsLicenseOwner'] %></RegisteredOwner> <% end -%> <TimeZone>Pacific Standard Time</TimeZone> </component> <component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <DomainProfile_EnableFirewall>false</DomainProfile_EnableFirewall> <PrivateProfile_EnableFirewall>false</PrivateProfile_EnableFirewall> <PublicProfile_EnableFirewall>false</PublicProfile_EnableFirewall> </component> <component name="Microsoft-Windows-ServerManager-SvrMgrNc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <DoNotOpenServerManagerAtLogon>true</DoNotOpenServerManagerAtLogon> </component> <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <fDenyTSConnections>false</fDenyTSConnections> </component> <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SecurityLayer>1</SecurityLayer> <UserAuthentication>0</UserAuthentication> </component> </settings> <settings pass="offlineServicing"> <component name="Microsoft-Windows-PnpCustomizationsNonWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <DriverPaths> <!--<PathAndCredentials wcm:action="add" wcm:keyValue="1"> <Path>c:\drivers</Path> </PathAndCredentials>--> </DriverPaths> </component> </settings> </unattend> - Type
- Snippet: no
- Type: provision
- Association
- Applicable OS: Windwows 2012 R2
This partition table will create Windows Server 2008 R2 and higher compatible partition (with a system reserved volume). It has 1 drive C:\ that will exand automatically and take all space on your disk. http:///ptabes -> New Operating System
- Name: Windows 2012 R2 - C:\ Entire Disk
- Layout:
<DiskConfiguration>
<Disk wcm:action="add">
<CreatePartitions>
<CreatePartition wcm:action="add">
<Type>Primary</Type>
<Order>1</Order>
<Size>100</Size>
</CreatePartition>
<CreatePartition wcm:action="add">
<Type>Primary</Type>
<Order>2</Order>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Active>true</Active>
<Format>NTFS</Format>
<Label>System</Label>
<Order>1</Order>
<PartitionID>1</PartitionID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Format>NTFS</Format>
<Label>SYSTEM</Label>
<Letter>C</Letter>
<Order>2</Order>
<PartitionID>2</PartitionID>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
<WillShowUI>OnError</WillShowUI>
</DiskConfiguration>
- Os family: Windows
http://foremanHost/media -> New Medium
-
Medium
- Name: Windows Server 2012 R2 Standard
- Path: http://wimagingHost/install/server-2012r2x64.standard
- OS Family: Windows
-
Locations
- - keep empty if you don't use locations.
http:///operatingsystems -> New Operating System. You have to create OS first, and then reopen it to assign Partition Table, Installation Media, etc.
-
Operating System
- Name: windows
- Major: 6
- Minor: 3
- Description: Windows Server 2012 R2
- OS Family: Windows
- Root PW Hash: MD5
- Architechtures: x64
-
Partition Table
- Windows 2012 R2 - C:\ Entire Disk (defined here)
-
Installation Media
- Windows Server 2012 R2 Enterprise (defined here)
-
Templates
Init-InstallSources.ps1: Copies ISO files to wimaging.Init-WorkWim.ps1: Initializes work .wim file from sourcesUpdate-All.ps1: Triggers full-cycle .wim process.Backup-WorkWim.ps1: Backs up current .wim (in case you don't want to loose your changes).Push-WimBoot.ps1: ?Pushes current work boot.wim to install dirrectory, which makes it available over network.Unmount-VHD.ps1: Unmounts VHDUpdate-BootAll.ps1Push-Wim.ps1: Pushes current work .wim to install dirrectory, which makes it available over network.Revert-WorkWim.ps1: Removes current work wim and copies one from install location.Save-InstallWim.ps1: Saves install.wim in case you want to keep a good one.MountUnmount-VHD.ps1: Mounts VHD -> Waits for user keystroke -> Unmounts VHD (Useful for debugging)MountUnmount-Wim.ps1: Mounts WIM -> Waits for user keystroke -> Unmounts WIM (Useful for debugging)Prepare-Image.ps1:Get-WimInfo.ps1: Query information about WIM (Images, etc)Init-PxeTools.ps1:Init-Updates.ps1:Add-Updates.ps1: Adds updates to current work WIM.Capture-Wim.ps1: Captures WIM from a mounted VHD.Add-Features.ps1: Adds features to current work WIM.Add-Tools.ps1: Adds tools to current work WIM.Add-ToolsVHD.ps1: Adds tools to VHD.Add-Drivers.ps1: Adds drivers to current work WIM.Unmount-Wim.ps1: Unmount currently mounted WIM.Get-DismFeatures.ps1