Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #clear out stale variables, in case we're running this again in the same session
- Get-Variable -Exclude PWD,*Preference | Remove-Variable -EA 0
- #Connect-VM allows you to start a VM and open its console window. Helpful for troubleshooting your builds.
- #You can diable this by commenting it out in Build-RefVM
- function Connect-VM
- {
- [CmdletBinding(DefaultParameterSetName='name')]
- param(
- [Parameter(ParameterSetName='name')]
- [Alias('cn')]
- [System.String[]]$ComputerName=$env:COMPUTERNAME,
- [Parameter(Position=0,
- Mandatory,ValueFromPipelineByPropertyName,
- ValueFromPipeline,ParameterSetName='name')]
- [Alias('VMName')]
- [System.String]$Name,
- [Parameter(Position=0,
- Mandatory,ValueFromPipelineByPropertyName,
- ValueFromPipeline,ParameterSetName='id')]
- [Alias('VMId','Guid')]
- [System.Guid]$Id,
- [Parameter(Position=0,Mandatory,
- ValueFromPipeline,ParameterSetName='inputObject')]
- [Microsoft.HyperV.PowerShell.VirtualMachine]$InputObject,
- [switch]$StartVM
- )
- begin
- {
- Write-Verbose "Initializing InstanceCount, InstanceCount = 0"
- $InstanceCount=0
- }
- process
- {
- try
- {
- foreach($computer in $ComputerName)
- {
- Write-Verbose "ParameterSetName is '$($PSCmdlet.ParameterSetName)'"
- if($PSCmdlet.ParameterSetName -eq 'name')
- {
- # Get the VM by Id if Name can convert to a guid
- if($Name -as [guid])
- {
- Write-Verbose "Incoming value can cast to guid"
- $vm = Get-VM -Id $Name -ErrorAction SilentlyContinue
- }
- else
- {
- $vm = Get-VM -Name $Name -ErrorAction SilentlyContinue
- }
- }
- elseif($PSCmdlet.ParameterSetName -eq 'id')
- {
- $vm = Get-VM -Id $Id -ErrorAction SilentlyContinue
- }
- else
- {
- $vm = $InputObject
- }
- if($vm)
- {
- Write-Verbose "Executing 'vmconnect.exe $computer $($vm.Name) -G $($vm.Id) -C $InstanceCount'"
- vmconnect.exe $computer $vm.Name -G $vm.Id -C $InstanceCount
- }
- else
- {
- Write-Verbose "Cannot find vm: '$Name'"
- }
- if($StartVM -and $vm)
- {
- if($vm.State -eq 'off')
- {
- Write-Verbose "StartVM was specified and VM state is 'off'. Starting VM '$($vm.Name)'"
- Start-VM -VM $vm
- }
- else
- {
- Write-Verbose "Starting VM '$($vm.Name)'. Skipping, VM is not not in 'off' state."
- }
- }
- $InstanceCount+=1
- Write-Verbose "InstanceCount = $InstanceCount"
- }
- }
- catch
- {
- Write-Error $_
- }
- }
- }
- #build the reference VM and capture it
- function Build-RefVM{
- param(
- [string]$VMName,
- [string]$MAC,
- $StartupRAM = 4GB,
- $VHDSize = 80GB,
- [string]$ScratchLocation = "e:\MDTRefBuildVM",
- [string]$SwitchName = "ExternalSwitch",
- [string]$ISO = "C:\MDTReferenceBuild\Boot\RefBuild-LiteTouchPE_x64.iso",
- #[string]$DeploymentShare = "\\DIT-MDT\MDTBuildLab$",
- [string]$LogFolder = "C:\MDTReferenceBuild\Logs"
- )
- #Make sure LiteTouchPE ISO exists where we think it does
- $StartVMTime=(GET-DATE)
- Write-Host (Get-Date)" ${VMName}: Checking for LiteTouchPE ISO"
- $ISOFileExist = Test-Path $ISO -ErrorAction SilentlyContinue
- If($ISOFileExist -like 'False'){
- Write-Host (Get-Date)" ${VMName}: Unable to find LiteTouch PE ISO at $ISO, exiting."
- BREAK
- }
- #Create VM scratch space
- Write-Host (Get-Date)" ${VMName}: Creating VM Scratch Space"
- $Null = New-Item -Path $ScratchLocation -ItemType Directory -Force -ErrorAction SilentlyContinue
- #Check if VMSwitch on host exists
- Write-Host (Get-Date)" ${VMName}: Checking if the switch exists"
- $VMSwitchExist = Get-VMSwitch -Name $SWitchName -ErrorAction SilentlyContinue
- If($VMSwitchExist.Name -ne $SWitchName){
- Write-Host (Get-Date)" ${VMName}: Unable to find VMSwitch, exit"
- BREAK
- }
- #Check if VM exists and delete it
- Write-Host (Get-Date)" ${VMName}: Check if VM exists and delete it"
- $VMexist = Get-VM -Name $VMName -ErrorAction SilentlyContinue
- If($VMexist.Name -eq $VMName){
- Write-Host (Get-Date)" ${VMName}: Removing VM and VHDX"
- $VMToRemove = $VM
- $FolderPath = $VM.path
- if($VMToRemove.state -like "Running"){Stop-VM $VMToRemove -Force}
- $VMToRemove | Remove-VM -Force
- $FolderPath | Remove-Item -Force -Recurse
- Write-Host (Get-Date)" ${VMName}: VM Removed"
- }
- Write-Host (Get-Date)" ${VMName}: Try to remove the VM folder anyways, just to be sure"
- Remove-Item $ScratchLocation\$VMName -Force -Recurse -ErrorAction SilentlyContinue
- #Create VM
- Write-Host (Get-Date)" ${VMName}: Creating new VM"
- $VM = New-VM –Name $VMname –MemoryStartupBytes $StartUpRAM -SwitchName $SWitchName -NewVHDPath "$ScratchLocation\$VMName\$VMName.vhdx" -NewVHDSizeBytes $VHDSize -Path $ScratchLocation
- Set-VM -Name $VMname -DynamicMemory -MemoryMaximumBytes 10GB
- #Set MAC, Boot ISO, Processor
- Write-Host (Get-Date)" ${VMName}: Setting up VM parameters"
- Set-VMNetworkAdapter -VMName $VMName -StaticMacAddress $MAC
- Set-VMDvdDrive -VMName $VMName -Path $ISO
- Set-VMProcessor –VMName $VMName –Count 4
- #Start the VM
- $sleepcount=0
- Write-Host (Get-Date)" ${VMName}: Starting VM"
- #Uncomment one of the two lines below.
- #The first line just starts the VM, the second starts and connects the VM so you can see the progress of your build
- #I recommend using the 2nd line in the beginning, so you can see errors immediately
- #Start-VM -VMName $VMName #start VM
- Connect-VM -VMName $VMName -StartVM #start VM and view it
- Write-Host (Get-Date)" ${VMName}: VM started. Waiting for it to finish. Every dot is a minute, every line is an hour."
- While ((Get-VM -Name $VMName).State -ne "Off"){
- Start-Sleep -Seconds 60
- if(($sleepcount++ % 60) -eq 0) { Write-Host " " }
- Write-Host "." -NoNewLine
- }
- Write-Host " "
- Write-Host (Get-Date)" ${VMName}: Reference Built Task finished"
- #Check if VM exists and delete it
- $VMexist = Get-VM -Name $VMName -ErrorAction SilentlyContinue
- If($VMexist.Name -eq $VMName){
- Write-Host (Get-Date)" ${VMName}: Removing VM and VHDX"
- $VMToRemove = $VM
- $FolderPath = $VM.path
- if($VMToRemove.state -like "Running"){Stop-VM $VMToRemove -Force}
- $VMToRemove | Remove-VM -Force
- $FolderPath | Remove-Item -Force -Recurse
- Write-Host (Get-Date)" ${VMName}: VM Removed"
- }
- #Delete the dynamic BDD.log since it's already in the log folder
- Write-Host (Get-Date)" ${VMName}: Deleting redundant BDD log"
- Remove-Item $LogFolder\$VMName\BDD.log
- $ElapsedTime = (Get-Date) - $StartVMTime
- Write-Host (Get-Date)" ${VMName}: Elapsed Time for reference build (VM only): $ElapsedTime"
- Write-Host (Get-Date)" ${VMName}: Moving captured WIM back to the deployment share"
- Move-Item C:\MDTReferenceBuild\Captures\$VMName\$VMName.wim "C:\MDTMachineDeploy\Operating Systems\$VMName" -Force
- }
- #Update our Virgin-from-ISO WIM to something that will take less time in MDT
- function Update-WIM{
- Param(
- [array]$Indexes,
- [string]$OS,
- #Set paths to make it look nicer, feel free to override these here as pass them in as parameters
- $RootPath = "e:\$OS",
- $WIMOrigPath = "$RootPath\wim-orig", #install.wim directly from the latest available ISO
- $WIMNewPath = "$RootPath\wim-new", #where the new WIM will end up
- $UpdatePath = "$RootPath\updates-1st", #first set of updates to install
- $SUpdatePath = "$RootPath\updates-2nd", #special/second updates to install after others
- $WIMMountPath = "$RootPath\wim-mount", #temporary directory where the WIM is mounted while you're DISMing is
- $WIMScratch = "$RootPath\wim-scratch", #scratch space on the same SSD to maximize throughput
- $RefBuildOSPath = "C:\MDTReferenceBuild\Operating Systems\$OS\Sources\", #where I've imported the virgin extracted ISOs to and where I'll put the updated install.wim
- $DISMLog = "$RootPath\logs\DISM-"+(get-date -format "yyyyMMdd")+".log", #path to save DISM logs named by date
- $DISMPath = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe"
- #We have to to use the Win10 DISM included in the ADK if we want to service Win10 WIMs. Just calling dism.exe gets us the system DISM
- #In my case, running this on Server 2012 R2, the system dism.exe can't handle Win10 WIMs
- )
- #Keep a copy of the WIM direct from the ISO so we can start from virgin every time
- Write-Host (Get-Date)" ${OS}: Copy the original ISO WIM to our scratch space"
- Copy-Item $WIMOrigPath\install.wim $WimNewPath -Force -ErrorAction Stop
- #Re: Indexes. WIMs have multiple "indexes" where they store files related to their OS
- #Server 2012's ISO has Standard, Datacenter, and Core versions of each in 1 WIM, Win7 has Pro, HomePrem, and others in its WIM
- #We have to DISM process each index one at a time. We passed in which indexes we wanted updated earlier
- Foreach ($index in $Indexes) {
- Write-Host (Get-Date)" ${OS} ($Index): Mount the WIM to be updated on Index $index"
- & $DISMPath /mount-wim /wimfile:$WimNewPath\install.wim /mountdir:$WIMMountPath /index:$index /LogPath:$DismLog /ScratchDir:$WIMScratch
- #Lets get the SP2ish Convenience Rollup integrated into the WIM first
- #for some reason it has to be installed in separate DISM commands
- If ($OS -like "*W7*"){
- Write-Host (Get-Date)" ${OS} ($Index): Installing Win7 SP2 Convenience Rollup"
- #April 2015 Servicing Stack Update
- & $DISMPath /Image:$WIMMountPath /Add-Package /PackagePath:$RootPath\updates-sp2\Windows6.1-KB3020369-x64.msu /LogPath:$DismLog /ScratchDir:$WIMScratch
- #March 2016 Windows Update Client
- & $DISMPath /Image:$WIMMountPath /Add-Package /PackagePath:$RootPath\updates-sp2\Windows6.1-KB3138612-x64.msu /LogPath:$DismLog /ScratchDir:$WIMScratch
- #May 2016 Convenience Rollup
- & $DISMPath /Image:$WIMMountPath /Add-Package /PackagePath:$RootPath\updates-sp2\Windows6.1-kb3125574-v4-x64.msu /LogPath:$DismLog /ScratchDir:$WIMScratch
- }
- #Put in the first set of updates
- Write-Host (Get-Date)" ${OS} ($Index): Installing updates from primary update path"
- & $DISMPath /image:$WIMMountPath /Add-Package /PackagePath:$UpdatePath /LogPath:$DismLog /ScratchDir:$WIMScratch
- #I enable the .Net3 framework for my LabTech agent, disable this if you don't need it
- If ($OS -like "*S2012*"){
- Write-Host (Get-Date)" ${OS} ($Index): Enable .Net3 Framework for Server 2012"
- & $DISMPath /image:$WIMMountPath /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:c:\sources\sxs /LogPath:$DismLog /ScratchDir:$WIMScratch
- }
- #By default, msu/cab files are processed by alpha, but I put some here to guarantee that some get installed after the first batch.
- Write-Host (Get-Date)" ${OS} ($Index): Installing updates from secondary update path"
- & $DISMPath /image:$WIMMountPath /Add-Package /PackagePath:$SUpdatePath /LogPath:$DismLog /ScratchDir:$WIMScratch
- Write-Host (Get-Date)" ${OS} ($Index): Unmount the WIM and commit changes"
- & $DISMPath /unmount-wim /mountdir:$WIMMountPath /commit /LogPath:$DismLog /ScratchDir:$WIMScratch
- }
- #clearing the wim-scratch directory because we're done with it
- Remove-Item $WIMScratch\* -recurse
- #just some stats to show what's changed
- $WIMOrigSize = [math]::round((Get-Item "$WIMOrigPath\install.wim").length/1GB,3)
- $WIMNewSize = [math]::round((Get-Item "$WIMNewPath\install.wim").length/1GB,3)
- $WIMSizeDiff = ($WIMNewSize - $WIMOrigSize)
- Write-Host (Get-Date)" ${OS}: Original WIM Size = $WIMOrigSize GB"
- Write-Host (Get-Date)" ${OS}: Updated WIM Size = $WIMNewSize GB"
- Write-Host (Get-Date)" ${OS}: SizeDifference = $WIMSizeDiff GB"
- #put install.wim back into the ReferenceBuild share so we have a head start with our reference image building
- Write-Host (Get-Date)" ${OS}: Copy the WIM back to the Reference Build share"
- Copy-Item $WimNewPath\install.wim "$RefBuildOSPath" -Force -ErrorAction Stop
- Write-Host (Get-Date)" ${OS}: Done processing and updating WIM"
- }
- #Check to see if we want to build WIMs and build them
- function MaybeBuildWIMS {
- If ($DoUpdateWIM_S2012R2STDX64 -eq $True) {
- Write-Host (Get-Date)" S2012R2STDX64: Updating Server 2012 R2 STD X64 WIM"
- Update-WIM -Indexes @(1,2) -OS "S2012R2STDX64"
- }
- If ($DoUpdateWIM_S2012R2ESSX64 -eq $True) {
- Write-Host (Get-Date)" S2012R2ESSX64: Updating Server 2012 R2 ESS X64 WIM"
- Update-WIM -Indexes @(1) -OS "S2012R2ESSX64"
- }
- If ($DoUpdateWIM_W7X64 -eq $True) {
- Write-Host (Get-Date)" W7X64: Updating Windows 7 X64 WIM"
- Update-WIM -Indexes @(2,3) -OS "W7X64"
- }
- If ($DoUpdateWIM_W10X64 -eq $True) {
- Write-Host (Get-Date)" W10X64: Updating Windows 10 X64 WIM"
- Update-WIM -Indexes @(1,2) -OS "W10X64"
- }
- Write-Host (Get-Date)" All WIMs up to date, lets build some VMs"
- }
- #Check to see if we want to build RefVMs and build them
- #The MAC Addresses here MUST match the ones in your CustomSettings.ini
- #for your Reference Build share It's how MDT knows which TaskSequence to use.
- #Re: MAC addresses. MS's OID for Hyper-V is 00-15-5d and your Hyper-V host
- #will automatically generate the 4th and 5th pieces of the default MAC address
- #range based on the last 2 octets of its IP. Here, I chose DE-1C because I
- #don't want to collide with other Hyper-V machines you may have and I'm betting
- #that your Hyper-V server isn't on X.X.222.28
- #If it is, change up these MAC addresses here *** AND IN YOUR CUSTOMSETTINGS.INI FILE ***
- function MaybeBuildRefVMs{
- If ($DoBuildRefVM_S2012R2STDX64 -eq $True) {
- Write-Host (Get-Date)" S2012R2STDX64: Starting Server 2012 R2 STD X64 reference build"
- Build-RefVM -VMname S2012R2STDX64 -MAC 00155DE1C810
- }
- If ($DoBuildRefVM_W7PROX64 -eq $True) {
- Write-Host (Get-Date)" W7PROX64: Starting Windows 7 Pro X64 reference build"
- Build-RefVM -VMname W7PROX64 -MAC 00155DE1C812
- }
- If ($DoBuildRefVM_W7HOMEPREMX64 -eq $True) {
- Write-Host (Get-Date)" W7HOMEPREMX64: Starting Windows 7 Home Prem X64 reference build"
- Build-RefVM -VMname W7HOMEPREMX64 -MAC 00155DE1C811
- }
- If ($DoBuildRefVM_S2012R2ESSX64 -eq $True) {
- Write-Host (Get-Date)" S2012R2ESSX64: Starting Server 2012 R2 ESS reference build"
- Build-RefVM -VMname S2012R2ESSX64 -MAC 00155DE1C813
- }
- If ($DoBuildRefVM_W10PROX64 -eq $True) {
- Write-Host (Get-Date)" W10PROX64: Starting Windows 10 Pro X64 reference build"
- Build-RefVM -VMname W10PROX64 -MAC 00155DE1C815
- }
- If ($DoBuildRefVM_W10HOMEX64 -eq $True) {
- Write-Host (Get-Date)" W10HOMEX64: Starting Windows 10 Home X64 reference build"
- Build-RefVM -VMname W10HOMEX64 -MAC 00155DE1C814
- }
- }
- $Starttime = (Get-Date)
- #Re: OS Names. The naming is important and to simplify, I use the OS name in many places.
- #W7PROX64 = W 7 PRO x64 = Windows 7 Professional x64
- #W7HOMEPREMX64 = W 7 HOMEPREM X64 = Windows 7 Home Premium x64
- #(You can't technically deploy Home Premium if you're not VLK licensed for it, but it works. Caveat Emptor.)
- #S2012R2STDX64 = S 2012 R2 STD X64 = Server 2012 R2 Standard x64
- #S2012R2ESSX64 = S 2012 R2 ESS X64 = Server 2012 R2 Essentials x64
- #You can use whatever naming scheme you want, but if you change anything,
- #make sure you change it EVERYWHERE else you make reference to it (in cs.ini too)
- #Flags to enable the building of WIMs and RefVMs, comment out to disable building this run
- #it is especially time-saving to build your WIMs once and then comment the WIM lines out
- #You'll only need to rebuild your WIMs if you drop new updates into the WIM updates folder, let WSUS get the rest
- $DoUpdateWIM_S2012R2STDX64 = $True
- $DoUpdateWIM_S2012R2ESSX64 = $True
- $DoUpdateWIM_W7X64 = $True
- $DoUpdateWIM_W10X64 = $True
- $DoBuildRefVM_S2012R2STDX64 = $True
- $DoBuildRefVM_S2012R2ESSX64 = $True
- $DoBuildRefVM_W7PROX64 = $True
- $DoBuildRefVM_W7HOMEPREMX64 = $True
- $DoBuildRefVM_W10HOMEX64 = $True
- $DoBuildRefVM_W10PROX64 = $True
- Write-Host (Get-Date)" Let's Go! Starting everything off at $Starttime"
- MaybeBuildWIMS
- MaybeBuildRefVMs
- $ElapsedTime = (Get-Date) - $Starttime
- Write-Host (Get-Date)" All Done! Elapsed Time: $ElapsedTime"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement