(C) Ondrej Sevecek, 2019 - www.sevecek.com, ondrej@sevecek.com
#===================== # Common machine config DBG ("Build step: {0}" -f $MyInvocation.MyCommand.Definition) DBG "Common machine config..." #=============== DBG ("Change video resolution: {0} | {1}" -f (Is-ValidString ($vmConfig.commonVM.videoRes)), $vmConfig.commonVM.videoRes) if (Is-ValidString ($vmConfig.commonVM.videoRes)) { $videResWidthHeight = Split-MultiValue $vmConfig.commonVM.videoRes [int] $videoWidth = Parse-IntSafe $videResWidthHeight[0] [int] $videoHeight = Parse-IntSafe $videResWidthHeight[1] DBG ('Requested video resolution: {0} x {1}' -f $videoWidth, $videoHeight) Define-Win32Api $win32rs = [Sevecek.Win32Api.User32]::ChangeDisplaySettings_Implemented($videoWidth, $videoHeight) DBGIF ('Change video resolution failed with: {0}' -f $win32rs) { $win32rs -ne 0 } } #=============== DBG ("Disable NetBIOS: {0}" -f (Parse-BoolSafe($vmConfig.commonVM.netbiosOff))) if (Parse-BoolSafe($vmConfig.commonVM.netbiosOff)) { $nics = Get-WMIQueryArray '.' "SELECT * FROM Win32_NetworkAdapterConfiguration" $nics | % { DBG ("Disabling NetBIOS on NIC: {0}, {1}" -f $_.Index, $_.Description) DBGSTART $wmiRs = $null $wmiRs = $_.SetTcpipNetbios(2) DBGER $MyInvocation.MyCommand.Name $error DBGEND DBGWMI $wmiRs } } #================= DBG ("Going to initialize preconfigured HDDs") #$offlineHDDs = Get-WMIQueryArray '.' "SELECT * FROM Win32_DiskDrive WHERE Signature = '0' OR Signature = NULL" | Sort-Object Index $initializedHDDs = Get-WMIQueryArray '.' "SELECT * FROM Win32_DiskDrive" | Sort-Object Index DBG ('Found all HDDs: {0}' -f $initializedHDDs.Count) DBG ('Determine OS disk: {0}' -f $env:SystemDrive) $osLogicalDisk = Get-WmiQuerySingleObject '.' ('SELECT * FROM Win32_LogicalDisk WHERE DeviceId = "{0}"' -f $env:SystemDrive) $osPartition = Get-WmiRelated $osLogicalDisk 'Win32_DiskPartition' [int] $osDiskIndex = $osPartition.DiskIndex DBG ('OS disk determined as: {0}' -f $osDiskIndex) DBGIF $MyInvocation.MyCommand.Name { Is-Null $osLogicalDisk } DBGIF $MyInvocation.MyCommand.Name { Is-Null $osPartition } foreach ($oneHDD in $initializedHDDs) { $diskID = $oneHDD.index DBG ("Processing disk: {0}, {1}, {2}, {3}, {4}, {5}, {6}" -f $oneHDD.Index, $oneHDD.InterfaceType, $oneHDD.Signature, ([int] ($oneHDD.Size / 1024 / 1024 / 1024)), $oneHDD.Caption, $oneHDD.Partitions, $oneHDD.DeviceID) DBGIF $MyInvocation.MyCommand.Name { ($oneHDD.InterfaceType -ne 'IDE') -and ($oneHDD.InterfaceType -ne 'SCSI') -and ($oneHDD.InterfaceType -ne 'HDC') -and ($oneHDD.InterfaceType -ne 'USB') -and ($oneHDD.InterfaceType -ne '1394') } [bool] $shouldSkipTheDisk = $oneHDD.Index -eq $osDiskIndex DBG ('Check if we should exclude this disk from being initialized: {0}' -f $shouldSkipTheDisk) if (-not $shouldSkipTheDisk) { $oneHDDPartitions = Get-WMIRelated $oneHDD 'Win32_DiskPartition' if ((Get-CountSafe $oneHDDPartitions) -gt 0) { foreach ($oneHddPartition in $oneHDDPartitions) { $oneHddLogicalDisks = Get-WMIRelated $oneHddPartition 'Win32_LogicalDisk' if ((Get-CountSafe $oneHddLogicalDisks) -gt 0) { foreach ($oneHddLogicalDisk in $oneHddLogicalDisks) { $possibleNoInitializationMarker = Join-Path $oneHddLogicalDisk.DeviceId $global:diskNoInitialization DBG ('One possible no-init marker: {0}' -f $possibleNoInitializationMarker) if (Test-Path $possibleNoInitializationMarker) { DBG ('Found a disk volume which excludes the disk from being initialized: {0}' -f $oneHddLogicalDisk.DeviceId) $shouldSkipTheDisk = $true } } } } } } if (($oneHDD.Partitions -gt 0) -and ($oneHDD.InterfaceType -ne 'IDE') -and (-not $shouldSkipTheDisk)) { if ($thisOSVersion -like '5.*') { # Will not online the disk on Windows XP as the OS # does not support SCSI media in Hyper-V IC. # but proceeds without an error $diskPartScr = @" list disk select disk $diskID online noerr "@ } else { # Note: Win6.x mounts the disks as read/only by default $diskPartScr = @" list disk select disk $diskID online disk noerr attributes disk clear readonly "@ } #$diskPartScrFile = Get-DataFileApp 'disk-part-online-disk' $null '.txt' #$diskPartScr.Replace("`n", "`r`n") | Set-Content -Path $diskPartScrFile -Force -EV er -EA SilentlyContinue #DBGER $MyInvocation.MyCommand.Name $er DBG ('Bring the disk ONLINE') # 0x80042807 = disk already online or rather cannot clear read/only attribute everywhere 6.1+ # 0x4 = cannot clear read/only attribute on 6.0 Call-DiskPart $diskPartScr ('disk-part-online-disk-{0:D2}' -f $oneHDD.Index) #@(0x80042807) #Run-Process 'DISKPART' ('/s "{0}"' -f $diskPartScrFile) -ignoreExitCodes @(0x80042807) DBG ("Giving it 4sec to assign a letter in a correct sequence if it happens automatically") Start-Sleep 4 if ($global:thisOSVersionNumber -lt 6) { # Note: On Windows 2003, the letters are not assigned automatically, # at least not on clean ISO install of 2003 R2 # $nonAssignedVolumes = Get-WMIQueryArray '.' 'SELECT * FROM Win32_Volume WHERE DriveLetter IS NULL AND Label <> "System Reserved"' DBG ('Found some volumes without assigned letters: {0}' -f (Get-CountSafe $nonAssignedVolumes)) foreach ($oneNonAssignedVolume in $nonAssignedVolumes) { DBG ('Assign next free drive letter to: {0}' -f $oneNonAssignedVolume.DeviceId) DBGSTART $wmiRs = $null $wmiRs = $oneNonAssignedVolume.AddMountPoint(('{0}:\' -f (Get-NextFreeDriveLetter))) DBGER $MyInvocation.MyCommand.Name $error DBGEND DBGWMI $wmiRs } } } else { DBG ('Skipping the disk as it either does not have any partitions on it, or is IDE attached, or is a system disk or is explicitly excluded') } } #================= DBG ('Going to initialize data HDDs from empty HDDs') $localAppDisks = ($vmConfig.SelectNodes('./*/app[@disks>0]') | Measure-Object -Maximum disks).Maximum DBG ('Initialize APP required data disks: {0}' -f ($localAppDisks)) if ($localAppDisks -gt 0) { $emptyHDDs = (Get-WMIQueryArray '.' "SELECT * FROM Win32_DiskDrive WHERE Partitions = '0'") | Sort-Object Index | Select-Object -First $localAppDisks # Note: in case the originally returned WMIQueryArray has just a single item, the Sort-Object and Select-Object # fucking behavior is to convert the result to a non-array. We cannot improve it with operator (,) as # it would create multidim array in the other cases. This sucks DBG ('Found empty HDDs: {0}' -f (Get-CountSafe $emptyHDDs)) $dataDiskId = 0 foreach ($oneHDD in $emptyHDDs) { $dataDiskId ++ if ($global:adaptingExistingEnv) { DBG ('We are adapting already build environment so check if a disk with the diskID is not already attached: {0}' -f $dataDiskId) [string] $markedVolumeDriveFound = Find-MarkedVolume ($global:datadiskMarker -f $dataDiskId) -driveType 3 if (Is-ValidString $markedVolumeDriveFound) { DBG ('We have found an already build data disk: {0}' -f $markedVolumeDriveFound) continue } } DBG ("Processing disk: {0}, {1}, {2}, {3}, {4}, {5}, {6}" -f $oneHDD.Index, $oneHDD.InterfaceType, $oneHDD.Signature, ([int] ($oneHDD.Size / 1024 / 1024 / 1024)), $oneHDD.Caption, $oneHDD.Partitions, $oneHDD.DeviceID) DBGIF $MyInvocation.MyCommand.Name { $oneHDD.InterfaceType -ne 'SCSI' } $diskID = $oneHDD.index $diskDeviceID = $oneHDD.DeviceID if ($thisOSVersion -like '5.*') { $diskPartScr = @" select disk $diskID online noerr convert gpt noerr create partition primary noerr assign noerr "@ } else { # Note: Win6.x mounts the disks as read/only by default $diskPartScr = @" select disk $diskID online disk noerr attributes disk clear readonly noerr convert gpt noerr create partition primary noerr assign noerr "@ } #$diskPartScrFile = Get-DataFileApp 'disk-part-online-data-disk' $null '.txt' #$diskPartScr.Replace("`n", "`r`n") | Set-Content -Path $diskPartScrFile -Force -EV er -EA SilentlyContinue #DBGER $MyInvocation.MyCommand.Name $er DBG ('Bring the empty disk ONLINE and create new PARTITION') #Run-Process 'DISKPART' ('/s "{0}"' -f $diskPartScrFile) Call-DiskPart $diskPartScr 'disk-part-online-data-disk' DBG ("Giving it 4sec to assign a letter in a correct sequence") Start-Sleep 4 DBG ('Find the assigned partition.') $newPartition = Get-WMIQuerySingleObject '.' ('Associators Of {{Win32_DiskDrive.DeviceID="{0}"}} WHERE ResultClass = Win32_DiskPartition' -f ($diskDeviceID.Replace('\', '\\'))) DBG ('Find the assigned letter.') $logicalDisk = Get-WMIQuerySingleObject '.' ('Associators Of {{Win32_DiskPartition.DeviceID="{0}"}} WHERE ResultClass = Win32_LogicalDisk' -f ($newPartition.DeviceID.Replace('\', '\\'))) DBG ('Letter determined: {0}' -f $logicalDisk.DeviceID) $newVolume = Get-WMIQuerySingleObject '.' ('SELECT * FROM Win32_Volume WHERE DriveLetter = "{0}" AND FileSystem = NULL' -f $logicalDisk.DeviceID) if (Is-NonNull $newVolume) { DBG ('Format volume: {0} | {1}' -f $newVolume.DriveLetter, $newVolume.DeviceID) DBGSTART $wmiRs = $null $wmiRs = $newVolume.Format('NTFS', $true, 0, "DATA$dataDiskId", $false) DBGER $MyInvocation.MyCommand.Name $error DBGEND DBGWMI $wmiRs DBG ('Create ID file') New-Item -Path (Join-Path $logicalDisk.DeviceID ($global:datadiskMarker -f $dataDiskId)) -ItemType File -Force -EV er -EA SilentlyContinue | Out-Null DBGER $MyInvocation.MyCommand.Name $er } } } Find-MarkedVolumes #=============== DBG ('Create all partitions required') $partitions = $vmConfig.SelectNodes('./*/partition') DBG ('Found partition requirements: {0}' -f (Get-CountSafe $partitions)) if ((Get-CountSafe $partitions) -gt 0) { foreach ($onePartition in $partitions) { DBG ('One partition: doNotCreate = {0} | letter = {1}' -f (-not (Parse-BoolSafe $onePartition.existing)), $onePartition.letter) if (-not (Parse-BoolSafe $onePartition.existing)) { Create-Partition $onePartition } } } #=============== DBG ('Update ISO disk letters') if (Is-ValidString $vmConfig.vm.iso) { $isos = Split-MultiValue $vmConfig.vm.iso DBG ('Found ISOs in config: {0}' -f (Get-CountSafe $isos)) $cdDeviceNumber = 0 foreach ($oneISO in $isos) { [string] $newDriveLetterPlusMarker = '' if (Has-ValueFlags $oneISO D ([ref] $newDriveLetterPlusMarker)) { $isoMarker = $newDriveLetterPlusMarker.SubString(1) $newDriveLetter = $newDriveLetterPlusMarker.SubString(0, 1) DBG ('ISO should be assigned drive letter: {0} | {1} | {2} | {3}' -f $cdDeviceNumber, $isoMarker, $oneISO, $newDriveLetter) [string] $currentDrive = '' # must be typed explicitly, because the latter .Drive[0] produces [char] instead of [string] if (Is-ValidString $isoMarker) { $currentDrive = (Find-MarkedVolume $isoMarker 5)[0] } if (Is-EmptyString $currentDrive) { # there might not be the ISO yet, in case nothing is found, we proceed with CDROM drive number method $currentDrive = (Get-WmiQueryArray '.' 'SELECT * FROM Win32_CDRomDrive')[$cdDeviceNumber].Drive[0] } DBG ('ISO found at: {0}' -f $currentDrive) DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $currentDrive } if (Is-ValidString $currentDrive) { if ($global:thisOSVersionNumber -eq 5.1) { # Note: on Windows XP, the Win32_Volume is not available so we have to make do with MOUNTVOL command instead DBG ('Remounting the drive with MOUNTVOL on Windows XP') $mountvolCmdPath = "$env:SystemRoot\System32\mountvol.exe" $mountvolOutput = (& $mountvolCmdPath ('{0}:\' -f $currentDrive) '/L') DBG ('MOUNTVOL output: {0}' -f ($mountvolOutput | Out-String)) $currentDriveSystemPath = $mountvolOutput.Trim() DBG ('Current drive system path: {0}' -f $currentDriveSystemPath) DBGIF $MyInvocation.MyCommand.Name { $currentDriveSystemPath -notmatch "\\\\\?\\Volume{$global:rxGUID}\\" } Run-Process $mountvolCmdPath ('"{0}:" /D' -f $currentDrive) Run-Process $mountvolCmdPath ('"{0}:" "{1}"' -f $newDriveLetter, $currentDriveSystemPath.SubString(0, ($currentDriveSystemPath.Length - 1))) } else { $isoVolume = Get-WmiQuerySingleObject '.' ('SELECT * FROM Win32_Volume WHERE DriveLetter = "{0}:"' -f $currentDrive) DBG ('ISO volume found: {0} | {1} | {2}' -f $isoVolume.Label, $isoVolume.DriveLetter, $isoVolume.FileSystem) DBG ('Set new drive letter: {0}' -f $newDriveLetter) DBGSTART $isoVolume.DriveLetter = '{0}:' -f $newDriveLetter $wmiRes = $isoVolume.Put() DBGER $MyInvocation.MyCommand.Name $error DBGEND } } } $cdDeviceNumber ++ } } #=============== DBG ("Will update installation source paths: {0}" -f ($thisOSVersion -like '5.*')) if ($thisOSVersion -like '5.*') { if ($thisOSRole -like '*workstation*') { DBG ('Clients get installed from DVD, find the installation media for Windows XP') $winSrcPath = Find-MarkedVolume 'WIN51' 5 DBGIF $MyInvocation.MyCommand.Name { $winSrcPath -ne (Find-MarkedVolume 'SETUP.EXE' 5) } DBGIF $MyInvocation.MyCommand.Name { $winSrcPath -ne (Find-MarkedVolume 'i386' 5) } DBG ("Updating source paths for XP: {0}, i386 exists = {1}" -f $winSrcPath, (Test-Path (Join-Path $winSrcPath i386))) } else { # WinSrcPath was originally pointing to DISK1, but we need to have both Disk1 and Disk2 in the same location # to allow for SYSOCMGR installation of R2 components #$winSrcPath = Join-Path $installMediaVolume 'Windows2003R2-SP2\win2k3r2-withsp2-disk1' $winSrcPath = Join-Path $installMediaVolume 'Windows2003R2-SP2' if (-not (Test-Path (Join-Path $winSrcPath i386))) { # FUCK LAZY JUNG, I have updated the installMedia.VHD but the lazy pig didn't updated the image DBG ('Path does not exist: {0}' -f (Join-Path $winSrcPath i386)) $winSrcPath = Join-Path $installMediaVolume 'Windows2003R2-SP2\win2k3r2-withsp2-disk1' DBG ('Fall back to older installation media path (FUCK LAZY JUNG): {0}' -f $winSrcPath) } DBG ("Updating source paths for 2003: {0}, i386 exists = {1}" -f $winSrcPath,(Test-Path (Join-Path $winSrcPath i386))) } Set-RegistryValue 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup' SourcePath $winSrcPath String Set-RegistryValue 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup' ServicePackSourcePath $winSrcPath String } DBG ('Cleanup filler files from all volumes') do { $fillerFound = '' $fillerFound = Find-MarkedVolume $global:emptyFiller 3 if (Is-ValidString $fillerFound) { DBG ('Remove filler file: {0}' -f $fillerFound) DBGSTART Remove-Item "$fillerFound\$global:emptyFiller" -EA SilentlyContinue if ($error.Count -gt 0) { $notDeleted = $true } DBGER $MyInvocation.MyCommand.Name $error DBGEND if ($notDeleted) { DBGIF 'Stopping filler deletions to prevent looping on a file that cannot be deleted' { $notDeleted } break } } else { DBG ('No other filler file found') } } while (Is-ValidString $fillerFound) DBG ('Should update PATH variable: {0}' -f (Is-ValidString $vmConfig.commonVM.path)) if (Is-ValidString $vmConfig.commonVM.path) { [string[]] $pathItems = Split-MultiValue $vmConfig.commonVM.path DBG ('Path items to add: {0}' -f $pathItems.Count) foreach ($onePathItem in $pathItems) { Set-EnvironmentVariable 'PATH' (Resolve-VolumePath (Strip-ValueFlags $onePathItem)) 'Machine' $true $true } } DBG ('Should mark install media volume(s) read/only: {0}' -f (Parse-BoolSafe $vmConfig.commonVM.installMediaRO)) if (Parse-BoolSafe $vmConfig.commonVM.installMediaRO) { $allInstallMediaVolumes = Find-MarkedVolume $global:installSourceMediaWildcard 3 -findAllWithWildcard foreach ($oneInstallMediaVolume in $allInstallMediaVolumes) { $imLD = Get-WmiQuerySingleObject '.' ('SELECT * FROM Win32_LogicalDisk WHERE DeviceId = "{0}"' -f $oneInstallMediaVolume) $imDP = Get-WmiRelatedSingle $imLD 'Win32_DiskPartition' $imDD = Get-WmiRelatedSingle $imDP 'Win32_DiskDrive' DBG ('Install media volume/partition/disk: {0} | {1} | {2} | {3} | {4} | {5} | {6} | {7}' -f $imLD.VolumeName, [Math]::Round(($imLD.Size / 1GB), 1), [Math]::Round(($imLD.FreeSpace / 1GB), 1), $imDP.Name, $imDP.BootPartition, $imDD.Name, $imDD.Index, $imDD.Signature) $imDiskId = $imDD.Index DBG ('Install media disk index found: {0}' -f $imDiskId) DBGIF $MyInvocation.MyCommand.Name { $imDP.Name -notlike ('Disk #{0}, Partition #*' -f $imDiskId) } DBGIF $MyInvocation.MyCommand.Name { $imDD.Name -notlike ('\\.\PHYSICALDRIVE{0}' -f $imDiskId) } if ($global:thisOSVersionNumber -gt 5.2) { $diskPartScr = @" SELECT DISK $imDiskId ATTRIBUTES DISK SET READONLY "@ Call-DiskPart $diskPartScr "disk-part-mark-install-media-read-only" } else { DBG ('Cannot use DISKPART ATTRIBUTES on Windows XP and older') DBG ('Cannot use DISKPART ATTRIBUTES DISK on Windows 2003') DBG ('Leaving install media writable as long as its permissions allow') } } } DBG ('Should we extend some volumes: {0} | {1}' -f (Is-ValidString $vmConfig.wks.extendVol), $vmConfig.wks.extendVol) if (Is-ValidString $vmConfig.wks.extendVol) { [string[]] $volumes = Split-MultiValue $vmConfig.wks.extendVol DBG ('Volumes to be extended: #{0} | {1}' -f $volumes.Length, ($volumes -join ',')) foreach ($oneVolume in $volumes) { $oneVolumeDisk = Split-Path -Qualifier (Resolve-VolumePath $oneVolume) DBGIF ('Weird volume path: {0}' -f $oneVolumeDisk) { $oneVolumeDisk -notlike '[a-z]:' } [string] $diskPartScr = @" SELECT VOLUME $oneVolumeDisk EXTEND "@ Call-DiskPart $diskPartScr 'disk-part-extend-volume' } } DBG ('Should we enhost all host IP addresses: {0}' -f (Parse-BoolSafe $vmConfig.commonVM.hostTheHost)) if (Parse-BoolSafe $vmConfig.commonVM.hostTheHost) { DBG ('Host IP addresses: {0} | {1}' -f $global:phaseCfg.sevecekBuildup.host.ips, (Count-MultiValue $global:phaseCfg.sevecekBuildup.host.ips)) $hostIPs = Split-MultiValue $global:phaseCfg.sevecekBuildup.host.ips DBGIF $MyInvocation.MyCommand.Name { (-not $global:adaptingExistingEnv) -and (Get-CountSafe $hostIPs) -lt 1 } if ((Get-CountSafe $hostIPs) -gt 0) { $atLeastOneIPv4Done = $false foreach ($oneHostIP in $hostIPs) { DBG ('One IP to host: {0}' -f $oneHostIP) DBGIF ('Weird IP address: {0}' -f $oneHostIP) { -not (Is-IPv4OrIPv6Address $oneHostIP) } if ($oneHostIP -match $global:rxIPv4) { $nameAndIPs = @{ 'hvhost' = ('A${0}' -f $oneHostIP) } Update-HostsFile $nameAndIPs $atLeastOneIPv4Done = $true } } DBGIF $MyInvocation.MyCommand.Name { -not $atLeastOneIPv4Done } } } DBG ('Should we configure monitor off and desktop lockout times manually: {0} | {1}' -f (Is-ValidString $vmConfig.monitorOff), $vmConfig.monitorOff) if (Is-ValidString $vmConfig.monitorOff) { Set-MonitorLockTimeout $vmConfig.monitorOff if ($global:thisOSVersionNumber -ge 6.2) { DBG ('Set the registry value to show the console lock timeout in the power control panel on Windows 6.2+') Set-RegistryValue 'HKLM:\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\8EC4B3A5-6868-48c2-BE75-4F3044BE88A7' Attributes 2 DWord } } #================= DBG ('Disable Windows Welcome Tour on Windows XP: {0}' -f (Parse-BoolSafe $vmConfig.noWelcomeTour)) if (Parse-BoolSafe $vmConfig.noWelcomeTour) { DBGIF $MyInvocation.MyCommand.Name { ($global:thisOSVersionNumber -gt 5.2) -or ($global:thisOSRole -notlike '*Workstation*') } Set-RegistryValue 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Applets\Tour' RunCount 0 DWord } #================= DBG ('Disable Sidebar on Windows Vista: {0}' -f (Parse-BoolSafe $vmConfig.sidebarOff)) if (Parse-BoolSafe $vmConfig.sidebarOff) { DBGIF $MyInvocation.MyCommand.Name { ($global:thisOSVersionNumber -ne 6.0) -or ($global:thisOSRole -notlike '*Workstation*') } Set-RegistryValue 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Windows\Sidebar' TurnOffSidebar 1 DWord } # SIG # Begin signature block # MIIc/QYJKoZIhvcNAQcCoIIc7jCCHOoCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBk1zOR6E7YtjPo # bvTyWkGidb0cc/GET3uN3dihzxclm6CCGAQwggTlMIIDzaADAgECAhA5vUKe0oFu # utW8yQO0umXnMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNVBAYTAklMMRYwFAYDVQQK # Ew1TdGFydENvbSBMdGQuMSkwJwYDVQQLEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9u # IEF1dGhvcml0eTEjMCEGA1UEAxMaU3RhcnRDb20gQ2xhc3MgMiBPYmplY3QgQ0Ew # HhcNMTYxMjAxMTU1MTEzWhcNMTgxMjAxMTU1MTEzWjBRMQswCQYDVQQGEwJDWjEa # MBgGA1UECAwRSmlob21vcmF2c2t5IEtyYWoxDTALBgNVBAcMBEJybm8xFzAVBgNV # BAMMDk9uZHJlaiBTZXZlY2VrMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC # AQEAr9E9hNj06bash9JX97kpsqK9Z/ciOBC6trI4nvlW9CPwhKBTb5wArhxLYZBG # 9jWPWrdy1nL/cm5qMqBb/mogYwMwvEYWMvsIOOVn6HD9lVhNAovD6PHz0ziBBKIs # zXTjyUPQaoIlIELovz967m78HJdUZJGxqhluAsS9o9/fEzA7XXUhUuqRKsetuZV/ # Asfh5sOveeoRsbeW4daTWvtz3TJuULL0w43LNVYJkd6LL8cegvLPVZUe1N7skvid # EvntdlowQsJlqFdrH3SGKIPKA6ObcY8SZWkEQSbVBF8Kum1UT+jN0gm+84FwOg5W # qKx+VvTK2ljVWnPrCD0Zzu2oIQIDAQABo4IBkzCCAY8wDgYDVR0PAQH/BAQDAgeA # MBMGA1UdJQQMMAoGCCsGAQUFBwMDMAkGA1UdEwQCMAAwHQYDVR0OBBYEFG2vSo3N # hQWILeUs0oN9XzHTejcfMB8GA1UdIwQYMBaAFD5ik5rXxxnuPo9JEIVVFSDjlIQc # MG0GCCsGAQUFBwEBBGEwXzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Auc3RhcnRz # c2wuY29tMDcGCCsGAQUFBzAChitodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0 # cy9zY2EuY29kZTIuY3J0MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuc3Rh # cnRzc2wuY29tL3NjYS1jb2RlMi5jcmwwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5z # dGFydHNzbC5jb20vMFEGA1UdIARKMEgwCAYGZ4EMAQQBMDwGCysGAQQBgbU3AQIF # MC0wKwYIKwYBBQUHAgEWH2h0dHBzOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kw # DQYJKoZIhvcNAQELBQADggEBAJuRiEvHtIYSpsmMkPhTz4QOOShN3p5KWdf8vm71 # A33CR9fds10d8D2B2aE+vjmHJ69GY0bbfg5oZY2Lsq2euL7Da5/hS8+6T3MEtD4h # njfHV7mxmoSfFuy/KDipoV6uwhI+ksqchXYdUH+5uCQO0MOO8ITjAgzUQsnZ4UIB # HBGeP+e+3ljxSYSXWdPIrgxdR971P/HhWSVfKNlmBgEKMQM5Jy0aAd4jxSl/AzdY # t0+6pliFJ1peGhdFni2Fm8fu5oN68aTIrNtc5WY7Lzgf+sRTVeWORWS37+1zAD0m # jzd8gyfBLxRuaRSfjYxny0rLXelAwfiA3ze2DU2Bfg9/rfcwggXYMIIDwKADAgEC # AhBsO9J+3TyUnpWOKKmzx1egMA0GCSqGSIb3DQEBCwUAMH0xCzAJBgNVBAYTAklM # MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQLEyJTZWN1cmUgRGlnaXRh # bCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBTdGFydENvbSBDZXJ0aWZp # Y2F0aW9uIEF1dGhvcml0eTAeFw0xNTEyMTYwMTAwMDVaFw0zMDEyMTYwMTAwMDVa # MHUxCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSkwJwYDVQQL # EyBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEjMCEGA1UEAxMaU3Rh # cnRDb20gQ2xhc3MgMiBPYmplY3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw # ggEKAoIBAQC5FARY97LFhiwIMmCtCCbAgXe5aBnZFSsdGGnk2hqWBZcuZHkaqT1R # M1rQd2r0ApNBw466cBur2Ht0b5jo17mpPmh2pImgIqwX1in4u7hhn9IH0GYOMEcg # K3ACHv5zCRxxNLXifqmsqKfxjjpABnaSyvd4bO9YBXN9f4NQ6aJVAuMArpanxsJk # e+P4WECVLk17v92CAN5JVaczI+baT/lgo5NVcTEkloCViSbIfU6ILeyhOSQZvpom # MYk8eJqI0nimOTJJfmXangNDsrX8np+3lXD0+6rCZisXRWIaeffyTMHZ31Qj1D50 # WYdRtX5yev4WgaXoKJQN3lkgXUcytvyHAgMBAAGjggFaMIIBVjAOBgNVHQ8BAf8E # BAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAy # BgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5j # cmwwZgYIKwYBBQUHAQEEWjBYMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5zdGFy # dHNzbC5jb20wMAYIKwYBBQUHMAKGJGh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2Nl # cnRzL2NhLmNydDAdBgNVHQ4EFgQUPmKTmtfHGe4+j0kQhVUVIOOUhBwwHwYDVR0j # BBgwFoAUTgvvGqRAW6UXaYcwyjRoQ9BBrvIwPwYDVR0gBDgwNjA0BgRVHSAAMCww # KgYIKwYBBQUHAgEWHmh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeTANBgkq # hkiG9w0BAQsFAAOCAgEAY6U81bNtJyjY67pTrzAL6kpdEtX5mspw+kxjjNdNVH5G # 6lLnhaEkIxqdpvY/Wdw+UdNtExs+N8efKPSwh2m/BxXj2fSeLMwXcwHFookScEER # 8ez0quCNzioqNHac7LCXPEnQzbtG2FHlePKNDWh8eU6KxiAzNzIrIxPthinHGgLT # BOACHQM2YTlD8YoU5oN3dLmBOqtH0BDMZoLcjEIoEW1zC+TnVb3yU1G0xub6gnN7 # lP50vbAiHJYrnywQiXaloBV8B9YYfe6ZgvjqxwufwFcMVyE3UmCuDTsOpjqDEKpJ # 25s+FUdkie5VqCS1aaudLo31X+9UvP45pfgyRqzyfUnVEhH4ZXxlBWZMzj2Xov5+ # m/+H3kxYuFA5xdqdshj/Zx00S7PkCSF+8M1NCcvFgQwjIw61bZAjDBl3P3a8xNTX # sb2CjFdiNKbT3LD6IGeIf0b/EbPf0FXdvBrxm0ofMOhnngdPolPYCtoOGtZPAVe/ # xeu+/ZyKv6TSHlshaUO0iYfsmbXnZ51vvt/kkjwms9/qPFxSuE0fjEfF7aQazwRE # Df2hiVPR0pAhvShtM3oU4XreEFEUWEYHs25fYV4WMmxkUKSgmSmwRq45tvtGH4LT # b5+cd+iLqK8rBQL0E6xaUjjGfsYx7bueIvqTvCkrQvoxMbn/qDHCiypowDVq6TAw # ggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0Et # MTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVT # MREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1w # IFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8 # s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7d # y4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGf # rvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5 # uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYu # RhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJC # kawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMB # Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIw # ggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdp # Y2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBl # ACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBu # AHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0 # AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0 # AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBl # AG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5 # ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABl # AHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwD # FTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpN # JLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMu # ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0 # dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3Js # MHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl # cnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v # RGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+ # GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94G # AYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFB # pr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dx # nSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bI # o4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQK # Ohvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJ # KoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu # YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQg # QXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAw # MFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE # CxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJ # RCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKV # pYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqv # y15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/ # 2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE # 7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKv # mPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIU # YJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIG # CCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcD # CDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUH # AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o # dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 # AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 # AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp # AGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl # AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo # AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg # AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg # AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB # /wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8v # b2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6 # MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy # ZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e # 36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqG # SIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90 # OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann # 4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qq # q8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD # 7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38 # KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIETzCCBEsCAQEwgYkwdTEL # MAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKTAnBgNVBAsTIFN0 # YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSMwIQYDVQQDExpTdGFydENv # bSBDbGFzcyAyIE9iamVjdCBDQQIQOb1CntKBbrrVvMkDtLpl5zANBglghkgBZQME # AgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEM # BgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqG # SIb3DQEJBDEiBCBZo/F9DLNHJLaXKHlUQb8MfZ0TcjKnCQlv4yPH3IzYwjANBgkq # hkiG9w0BAQEFAASCAQAOd4cDUFY06/7Wr5Ch9vfNC5nCAhH10LTysPJloM3vAUxy # rc212Kz+bCVaY4QBZhJy1+R5tAjWgsAjqB5a3kOXntQ50XwWFeLdxbI9H4eCGIeh # uSNfC5grYPPaLojVaEUDgGMvRPWrXjqmTYOzl/qgr7KDqQ95tq0fHbDcYyesh9oc # TNYLKCdLDHqF46/gHIuKiH1lf900MiHNrgm3qlbP4hAdVQ8riCcXbGpjSxvV4bQp # 54XNi0mEKFqQOzB0aye46dhVRhmLrhhj9bvI//ppDKp5BDoal9QW3riioaNJtflC # BNiXrwVF+XOujgSVv/MDjOzA5BzSEDBH3Uq8dP0KoYICDzCCAgsGCSqGSIb3DQEJ # BjGCAfwwggH4AgEBMHYwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0 # IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNl # cnQgQXNzdXJlZCBJRCBDQS0xAhADAZoCOv9YsWvW1ermF/BmMAkGBSsOAwIaBQCg # XTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xODAx # MzAwNjE1NTlaMCMGCSqGSIb3DQEJBDEWBBSArCkPHqTJO7TCfx1nrnmlFYtctzAN # BgkqhkiG9w0BAQEFAASCAQAKfeoyeIuJ27I++71kH9eKrMz+B0z/eAMJd8GLcKuM # XmsmEhORXp4hzCu2QUWzUpmaFg9gC157CsXFeCu3gz/pCGjpWFcZCMhJvE+dq0Aj # y6nZhWfS+GIK73bce2QDUDKVp2pX7KS3oRE0kRJFD0v+PKZxxb+qpE/7UuH0+Wij # JQBIGpey4oKodMhUJEWIEgwXUw7hduYH6XmMVhOibn/Qrn6Lt4tvuEtaBQ/Gp09N # rpDBholC9GZk0zKBjnyqtawgMkqK118bDuRZN9lLw9XkTa6/cWbk723ksA3lhw3l # DRGoYPa909eqNkrZ2BuU2bM41qnN4j3loCfDKU7xONg9 # SIG # End signature block