Advertisement
Guest User

Untitled

a guest
May 25th, 2016
921
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #clear out stale variables, in case we're running this again in the same session
  2. Get-Variable -Exclude PWD,*Preference | Remove-Variable -EA 0
  3.  
  4. #Connect-VM allows you to start a VM and open its console window. Helpful for troubleshooting your builds.
  5. #You can diable this by commenting it out in Build-RefVM
  6. function Connect-VM
  7. {
  8.   [CmdletBinding(DefaultParameterSetName='name')]
  9.  
  10.   param(
  11.     [Parameter(ParameterSetName='name')]
  12.     [Alias('cn')]
  13.     [System.String[]]$ComputerName=$env:COMPUTERNAME,
  14.  
  15.     [Parameter(Position=0,
  16.         Mandatory,ValueFromPipelineByPropertyName,
  17.         ValueFromPipeline,ParameterSetName='name')]
  18.     [Alias('VMName')]
  19.     [System.String]$Name,
  20.  
  21.     [Parameter(Position=0,
  22.         Mandatory,ValueFromPipelineByPropertyName,
  23.         ValueFromPipeline,ParameterSetName='id')]
  24.     [Alias('VMId','Guid')]
  25.     [System.Guid]$Id,
  26.  
  27.     [Parameter(Position=0,Mandatory,
  28.         ValueFromPipeline,ParameterSetName='inputObject')]
  29.     [Microsoft.HyperV.PowerShell.VirtualMachine]$InputObject,
  30.  
  31.     [switch]$StartVM
  32.   )
  33.  
  34.   begin
  35.   {
  36.     Write-Verbose "Initializing InstanceCount, InstanceCount = 0"
  37.     $InstanceCount=0
  38.   }
  39.  
  40.   process
  41.   {
  42.     try
  43.     {
  44.       foreach($computer in $ComputerName)
  45.       {
  46.         Write-Verbose "ParameterSetName is '$($PSCmdlet.ParameterSetName)'"
  47.  
  48.         if($PSCmdlet.ParameterSetName -eq 'name')
  49.         {
  50.               # Get the VM by Id if Name can convert to a guid
  51.               if($Name -as [guid])
  52.               {
  53.          Write-Verbose "Incoming value can cast to guid"
  54.          $vm = Get-VM -Id $Name -ErrorAction SilentlyContinue
  55.               }
  56.               else
  57.               {
  58.          $vm = Get-VM -Name $Name -ErrorAction SilentlyContinue
  59.               }
  60.         }
  61.         elseif($PSCmdlet.ParameterSetName -eq 'id')
  62.         {
  63.               $vm = Get-VM -Id $Id -ErrorAction SilentlyContinue
  64.         }
  65.         else
  66.         {
  67.           $vm = $InputObject
  68.         }
  69.  
  70.         if($vm)
  71.         {
  72.           Write-Verbose "Executing 'vmconnect.exe $computer $($vm.Name) -G $($vm.Id) -C $InstanceCount'"
  73.           vmconnect.exe $computer $vm.Name -G $vm.Id -C $InstanceCount
  74.         }
  75.         else
  76.         {
  77.           Write-Verbose "Cannot find vm: '$Name'"
  78.         }
  79.  
  80.         if($StartVM -and $vm)
  81.         {
  82.           if($vm.State -eq 'off')
  83.           {
  84.             Write-Verbose "StartVM was specified and VM state is 'off'. Starting VM '$($vm.Name)'"
  85.             Start-VM -VM $vm
  86.           }
  87.           else
  88.           {
  89.             Write-Verbose "Starting VM '$($vm.Name)'. Skipping, VM is not not in 'off' state."
  90.           }
  91.         }
  92.  
  93.         $InstanceCount+=1
  94.         Write-Verbose "InstanceCount = $InstanceCount"
  95.       }
  96.     }
  97.     catch
  98.     {
  99.       Write-Error $_
  100.     }
  101.   }
  102. }
  103.  
  104. #build the reference VM and capture it
  105. function Build-RefVM{
  106.     param(
  107.         [string]$VMName,
  108.         [string]$MAC,
  109.         $StartupRAM = 4GB,
  110.         $VHDSize = 80GB,
  111.         [string]$ScratchLocation = "e:\MDTRefBuildVM",
  112.         [string]$SwitchName = "ExternalSwitch",
  113.         [string]$ISO = "C:\MDTReferenceBuild\Boot\RefBuild-LiteTouchPE_x64.iso",
  114.         #[string]$DeploymentShare = "\\DIT-MDT\MDTBuildLab$",
  115.         [string]$LogFolder = "C:\MDTReferenceBuild\Logs"
  116.     )
  117.  
  118.     #Make sure LiteTouchPE ISO exists where we think it does
  119.     $StartVMTime=(GET-DATE)
  120.     Write-Host (Get-Date)" ${VMName}: Checking for LiteTouchPE ISO"
  121.     $ISOFileExist = Test-Path $ISO -ErrorAction SilentlyContinue
  122.         If($ISOFileExist -like 'False'){
  123.             Write-Host (Get-Date)" ${VMName}: Unable to find LiteTouch PE ISO at $ISO, exiting."
  124.             BREAK
  125.         }
  126.  
  127.     #Create VM scratch space
  128.     Write-Host (Get-Date)" ${VMName}: Creating VM Scratch Space"
  129.     $Null = New-Item -Path $ScratchLocation -ItemType Directory -Force -ErrorAction SilentlyContinue
  130.  
  131.     #Check if VMSwitch on host exists
  132.     Write-Host (Get-Date)" ${VMName}: Checking if the switch exists"
  133.     $VMSwitchExist = Get-VMSwitch -Name $SWitchName -ErrorAction SilentlyContinue
  134.         If($VMSwitchExist.Name -ne $SWitchName){
  135.             Write-Host (Get-Date)" ${VMName}: Unable to find VMSwitch, exit"
  136.             BREAK
  137.         }
  138.  
  139.     #Check if VM exists and delete it
  140.     Write-Host (Get-Date)" ${VMName}: Check if VM exists and delete it"
  141.     $VMexist = Get-VM -Name $VMName -ErrorAction SilentlyContinue
  142.     If($VMexist.Name -eq $VMName){
  143.         Write-Host (Get-Date)" ${VMName}: Removing VM and VHDX"
  144.         $VMToRemove = $VM
  145.         $FolderPath = $VM.path
  146.         if($VMToRemove.state -like "Running"){Stop-VM $VMToRemove -Force}
  147.         $VMToRemove | Remove-VM -Force
  148.         $FolderPath | Remove-Item -Force -Recurse
  149.         Write-Host (Get-Date)" ${VMName}: VM Removed"
  150.     }
  151.  
  152.     Write-Host (Get-Date)" ${VMName}: Try to remove the VM folder anyways, just to be sure"
  153.     Remove-Item $ScratchLocation\$VMName -Force -Recurse -ErrorAction SilentlyContinue
  154.    
  155.     #Create VM
  156.     Write-Host (Get-Date)" ${VMName}: Creating new VM"
  157.     $VM = New-VM –Name $VMname –MemoryStartupBytes $StartUpRAM -SwitchName $SWitchName -NewVHDPath "$ScratchLocation\$VMName\$VMName.vhdx" -NewVHDSizeBytes $VHDSize -Path $ScratchLocation
  158.     Set-VM -Name $VMname -DynamicMemory -MemoryMaximumBytes 10GB
  159.  
  160.     #Set MAC, Boot ISO, Processor
  161.     Write-Host (Get-Date)" ${VMName}: Setting up VM parameters"
  162.     Set-VMNetworkAdapter -VMName $VMName -StaticMacAddress $MAC
  163.     Set-VMDvdDrive -VMName $VMName -Path $ISO
  164.     Set-VMProcessor –VMName $VMName –Count 4
  165.  
  166.     #Start the VM
  167.     $sleepcount=0
  168.     Write-Host (Get-Date)" ${VMName}: Starting VM"
  169.    
  170.     #Uncomment one of the two lines below.
  171.     #The first line just starts the VM, the second starts and connects the VM so you can see the progress of your build
  172.     #I recommend using the 2nd line in the beginning, so you can see errors immediately
  173.    
  174.     #Start-VM -VMName $VMName             #start VM
  175.     Connect-VM -VMName $VMName -StartVM   #start VM and view it
  176.  
  177.     Write-Host (Get-Date)" ${VMName}: VM started. Waiting for it to finish. Every dot is a minute, every line is an hour."
  178.     While ((Get-VM -Name $VMName).State -ne "Off"){
  179.         Start-Sleep -Seconds 60
  180.         if(($sleepcount++ % 60) -eq 0) { Write-Host " " }
  181.         Write-Host "." -NoNewLine
  182.     }
  183.     Write-Host " "
  184.     Write-Host (Get-Date)" ${VMName}: Reference Built Task finished"
  185.  
  186.     #Check if VM exists and delete it
  187.     $VMexist = Get-VM -Name $VMName -ErrorAction SilentlyContinue
  188.     If($VMexist.Name -eq $VMName){
  189.         Write-Host (Get-Date)" ${VMName}: Removing VM and VHDX"
  190.         $VMToRemove = $VM
  191.         $FolderPath = $VM.path
  192.         if($VMToRemove.state -like "Running"){Stop-VM $VMToRemove -Force}
  193.         $VMToRemove | Remove-VM -Force
  194.         $FolderPath | Remove-Item -Force -Recurse
  195.         Write-Host (Get-Date)" ${VMName}: VM Removed"
  196.     }
  197.    
  198.     #Delete the dynamic BDD.log since it's already in the log folder
  199.     Write-Host (Get-Date)" ${VMName}: Deleting redundant BDD log"
  200.     Remove-Item $LogFolder\$VMName\BDD.log
  201.    
  202.     $ElapsedTime = (Get-Date) - $StartVMTime
  203.     Write-Host (Get-Date)" ${VMName}: Elapsed Time for reference build (VM only): $ElapsedTime"
  204.  
  205.     Write-Host (Get-Date)" ${VMName}: Moving captured WIM back to the deployment share"
  206.     Move-Item C:\MDTReferenceBuild\Captures\$VMName\$VMName.wim "C:\MDTMachineDeploy\Operating Systems\$VMName" -Force
  207. }
  208.  
  209. #Update our Virgin-from-ISO WIM to something that will take less time in MDT
  210. function Update-WIM{
  211.     Param(
  212.     [array]$Indexes,
  213.     [string]$OS,
  214.     #Set paths to make it look nicer, feel free to override these here as pass them in as parameters
  215.     $RootPath = "e:\$OS",
  216.     $WIMOrigPath = "$RootPath\wim-orig",   #install.wim directly from the latest available ISO
  217.     $WIMNewPath =  "$RootPath\wim-new",    #where the new WIM will end up
  218.     $UpdatePath = "$RootPath\updates-1st",     #first set of updates to install
  219.     $SUpdatePath = "$RootPath\updates-2nd",   #special/second updates to install after others
  220.     $WIMMountPath = "$RootPath\wim-mount", #temporary directory where the WIM is mounted while you're DISMing is
  221.     $WIMScratch = "$RootPath\wim-scratch", #scratch space on the same SSD to maximize throughput
  222.     $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
  223.     $DISMLog = "$RootPath\logs\DISM-"+(get-date -format "yyyyMMdd")+".log",   #path to save DISM logs named by date
  224.     $DISMPath = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe"
  225.     #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
  226.     #In my case, running this on Server 2012 R2, the system dism.exe can't handle Win10 WIMs
  227.     )
  228.    
  229.     #Keep a copy of the WIM direct from the ISO so we can start from virgin every time
  230.     Write-Host (Get-Date)" ${OS}: Copy the original ISO WIM to our scratch space"
  231.     Copy-Item  $WIMOrigPath\install.wim $WimNewPath -Force -ErrorAction Stop
  232.  
  233.     #Re: Indexes. WIMs have multiple "indexes" where they store files related to their OS
  234.     #Server 2012's ISO has Standard, Datacenter, and Core versions of each in 1 WIM, Win7 has Pro, HomePrem, and others in its WIM
  235.     #We have to DISM process each index one at a time. We passed in which indexes we wanted updated earlier
  236.  
  237.     Foreach ($index in $Indexes) {
  238.         Write-Host (Get-Date)" ${OS} ($Index): Mount the WIM to be updated on Index $index"
  239.         & $DISMPath /mount-wim /wimfile:$WimNewPath\install.wim /mountdir:$WIMMountPath /index:$index /LogPath:$DismLog /ScratchDir:$WIMScratch
  240.        
  241.         #Lets get the SP2ish Convenience Rollup integrated into the WIM first
  242.         #for some reason it has to be installed in separate DISM commands
  243.         If ($OS -like "*W7*"){
  244.             Write-Host (Get-Date)" ${OS} ($Index): Installing Win7 SP2 Convenience Rollup"
  245.             #April 2015 Servicing Stack Update
  246.             & $DISMPath /Image:$WIMMountPath /Add-Package /PackagePath:$RootPath\updates-sp2\Windows6.1-KB3020369-x64.msu /LogPath:$DismLog /ScratchDir:$WIMScratch
  247.             #March 2016 Windows Update Client
  248.             & $DISMPath /Image:$WIMMountPath /Add-Package /PackagePath:$RootPath\updates-sp2\Windows6.1-KB3138612-x64.msu /LogPath:$DismLog /ScratchDir:$WIMScratch
  249.             #May 2016 Convenience Rollup
  250.             & $DISMPath /Image:$WIMMountPath /Add-Package /PackagePath:$RootPath\updates-sp2\Windows6.1-kb3125574-v4-x64.msu /LogPath:$DismLog /ScratchDir:$WIMScratch
  251.             }
  252.  
  253.         #Put in the first set of updates
  254.         Write-Host (Get-Date)" ${OS} ($Index): Installing updates from primary update path"
  255.         & $DISMPath /image:$WIMMountPath /Add-Package /PackagePath:$UpdatePath /LogPath:$DismLog /ScratchDir:$WIMScratch
  256.    
  257.         #I enable the .Net3 framework for my LabTech agent, disable this if you don't need it
  258.         If ($OS -like "*S2012*"){
  259.             Write-Host (Get-Date)" ${OS} ($Index): Enable .Net3 Framework for Server 2012"
  260.             & $DISMPath /image:$WIMMountPath /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:c:\sources\sxs /LogPath:$DismLog /ScratchDir:$WIMScratch
  261.             }
  262.  
  263.         #By default, msu/cab files are processed by alpha, but I put some here to guarantee that some get installed after the first batch.
  264.         Write-Host (Get-Date)" ${OS} ($Index): Installing updates from secondary update path"
  265.         & $DISMPath /image:$WIMMountPath /Add-Package /PackagePath:$SUpdatePath /LogPath:$DismLog /ScratchDir:$WIMScratch
  266.  
  267.         Write-Host (Get-Date)" ${OS} ($Index): Unmount the WIM and commit changes"
  268.         & $DISMPath /unmount-wim /mountdir:$WIMMountPath /commit /LogPath:$DismLog /ScratchDir:$WIMScratch
  269.         }
  270.  
  271.         #clearing the wim-scratch directory because we're done with it
  272.         Remove-Item $WIMScratch\* -recurse
  273.  
  274.         #just some stats to show what's changed
  275.         $WIMOrigSize = [math]::round((Get-Item "$WIMOrigPath\install.wim").length/1GB,3)
  276.         $WIMNewSize = [math]::round((Get-Item "$WIMNewPath\install.wim").length/1GB,3)
  277.         $WIMSizeDiff = ($WIMNewSize - $WIMOrigSize)
  278.  
  279.         Write-Host (Get-Date)" ${OS}: Original WIM Size = $WIMOrigSize GB"
  280.         Write-Host (Get-Date)" ${OS}: Updated WIM Size  = $WIMNewSize GB"
  281.         Write-Host (Get-Date)" ${OS}: SizeDifference    = $WIMSizeDiff GB"
  282.        
  283.         #put install.wim back into the ReferenceBuild share so we have a head start with our reference image building
  284.         Write-Host (Get-Date)" ${OS}: Copy the WIM back to the Reference Build share"
  285.         Copy-Item $WimNewPath\install.wim "$RefBuildOSPath" -Force -ErrorAction Stop
  286.        
  287.         Write-Host (Get-Date)" ${OS}: Done processing and updating WIM"
  288.         }
  289.  
  290. #Check to see if we want to build WIMs and build them
  291. function MaybeBuildWIMS {
  292.     If ($DoUpdateWIM_S2012R2STDX64 -eq $True) {
  293.         Write-Host (Get-Date)" S2012R2STDX64: Updating Server 2012 R2 STD X64 WIM"
  294.         Update-WIM -Indexes @(1,2) -OS "S2012R2STDX64"
  295.         }
  296.  
  297.     If ($DoUpdateWIM_S2012R2ESSX64 -eq $True) {
  298.         Write-Host (Get-Date)" S2012R2ESSX64: Updating Server 2012 R2 ESS X64 WIM"
  299.         Update-WIM -Indexes @(1) -OS "S2012R2ESSX64"
  300.         }
  301.  
  302.     If ($DoUpdateWIM_W7X64 -eq $True) {
  303.         Write-Host (Get-Date)" W7X64: Updating Windows 7 X64 WIM"
  304.         Update-WIM -Indexes @(2,3) -OS "W7X64"
  305.         }
  306.  
  307.     If ($DoUpdateWIM_W10X64 -eq $True) {
  308.         Write-Host (Get-Date)" W10X64: Updating Windows 10 X64 WIM"
  309.         Update-WIM -Indexes @(1,2) -OS "W10X64"
  310.         }
  311.  
  312.     Write-Host (Get-Date)" All WIMs up to date, lets build some VMs"
  313. }
  314.  
  315. #Check to see if we want to build RefVMs and build them
  316. #The MAC Addresses here MUST match the ones in your CustomSettings.ini
  317. #for your Reference Build share It's how MDT knows which TaskSequence to use.
  318.  
  319. #Re: MAC addresses. MS's OID for Hyper-V is 00-15-5d and your Hyper-V host
  320. #will automatically generate the 4th and 5th pieces of the default MAC address
  321. #range based on the last 2 octets of its IP. Here, I chose DE-1C because I
  322. #don't want to collide with other Hyper-V machines you may have and I'm betting
  323. #that your Hyper-V server isn't on X.X.222.28
  324. #If it is, change up these MAC addresses here *** AND IN YOUR CUSTOMSETTINGS.INI FILE ***
  325. function MaybeBuildRefVMs{
  326.     If ($DoBuildRefVM_S2012R2STDX64 -eq $True) {
  327.         Write-Host (Get-Date)" S2012R2STDX64: Starting Server 2012 R2 STD X64 reference build"
  328.         Build-RefVM -VMname S2012R2STDX64 -MAC 00155DE1C810
  329.         }
  330.  
  331.     If ($DoBuildRefVM_W7PROX64 -eq $True) {
  332.         Write-Host (Get-Date)" W7PROX64: Starting Windows 7 Pro X64 reference build"
  333.         Build-RefVM -VMname W7PROX64 -MAC 00155DE1C812
  334.         }
  335.  
  336.     If ($DoBuildRefVM_W7HOMEPREMX64 -eq $True) {    
  337.         Write-Host (Get-Date)" W7HOMEPREMX64: Starting Windows 7 Home Prem X64 reference build"
  338.         Build-RefVM -VMname W7HOMEPREMX64 -MAC 00155DE1C811
  339.         }
  340.  
  341.     If ($DoBuildRefVM_S2012R2ESSX64 -eq $True) {
  342.         Write-Host (Get-Date)" S2012R2ESSX64: Starting Server 2012 R2 ESS reference build"
  343.         Build-RefVM -VMname S2012R2ESSX64 -MAC 00155DE1C813
  344.         }
  345.  
  346.     If ($DoBuildRefVM_W10PROX64 -eq $True) {
  347.         Write-Host (Get-Date)" W10PROX64: Starting Windows 10 Pro X64 reference build"
  348.         Build-RefVM -VMname W10PROX64 -MAC 00155DE1C815
  349.         }
  350.  
  351.     If ($DoBuildRefVM_W10HOMEX64 -eq $True) {
  352.         Write-Host (Get-Date)" W10HOMEX64: Starting Windows 10 Home X64 reference build"
  353.         Build-RefVM -VMname W10HOMEX64 -MAC 00155DE1C814
  354.         }
  355. }
  356.  
  357. $Starttime = (Get-Date)
  358.  
  359. #Re: OS Names. The naming is important and to simplify, I use the OS name in many places.
  360. #W7PROX64 = W 7 PRO x64 = Windows 7 Professional x64
  361. #W7HOMEPREMX64 = W 7 HOMEPREM X64 = Windows 7 Home Premium x64
  362. #(You can't technically deploy Home Premium if you're not VLK licensed for it, but it works. Caveat Emptor.)
  363. #S2012R2STDX64 = S 2012 R2 STD X64 = Server 2012 R2 Standard x64
  364. #S2012R2ESSX64 = S 2012 R2 ESS X64 = Server 2012 R2 Essentials x64
  365.  
  366. #You can use whatever naming scheme you want, but if you change anything,
  367. #make sure you change it EVERYWHERE else you make reference to it (in cs.ini too)
  368.  
  369. #Flags to enable the building of WIMs and RefVMs, comment out to disable building this run
  370. #it is especially time-saving to build your WIMs once and then comment the WIM lines out
  371. #You'll only need to rebuild your WIMs if you drop new updates into the WIM updates folder, let WSUS get the rest
  372.  
  373. $DoUpdateWIM_S2012R2STDX64 = $True
  374. $DoUpdateWIM_S2012R2ESSX64 = $True
  375. $DoUpdateWIM_W7X64 = $True
  376. $DoUpdateWIM_W10X64 = $True
  377.  
  378. $DoBuildRefVM_S2012R2STDX64 = $True
  379. $DoBuildRefVM_S2012R2ESSX64 = $True
  380. $DoBuildRefVM_W7PROX64 = $True
  381. $DoBuildRefVM_W7HOMEPREMX64 = $True
  382. $DoBuildRefVM_W10HOMEX64 = $True
  383. $DoBuildRefVM_W10PROX64 = $True
  384.  
  385. Write-Host (Get-Date)" Let's Go! Starting everything off at $Starttime"
  386.  
  387. MaybeBuildWIMS
  388. MaybeBuildRefVMs
  389.  
  390. $ElapsedTime = (Get-Date) - $Starttime
  391. Write-Host (Get-Date)" All Done! Elapsed Time: $ElapsedTime"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement