############################################################################################################################# # # ESXi-Customizer-PS.ps1 - a script to build a customized ESXi installation ISO using ImageBuilder # # Version: 2.3 # Author: Andreas Peetz (ESXi-Customizer-PS@v-front.de) # Info/Tutorial: http://esxi-customizer-ps.v-front.de/ # # License: # # This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # A copy of the GNU General Public License is available at http://www.gnu.org/licenses/. # ############################################################################################################################# # # NOTE: This script is SIGNED. Please remove the signature block at the end of the file before modifying it! # ############################################################################################################################# param( [string]$iZip = "", [string]$pkgDir = "", [string]$obDir = "", [string]$outDir = $(Split-Path $MyInvocation.MyCommand.Path), [string]$ipname = "", [string]$ipvendor = "", [string]$ipdesc = "", [switch]$vft = $false, [string[]]$dpt = @(), [string[]]$load = @(), [switch]$test = $false, [switch]$sip = $false, [switch]$nsc = $false, [switch]$help = $false, [switch]$ozip = $false, [switch]$v50 = $false, [switch]$v51 = $false, [switch]$v55 = $false, [switch]$v60 = $false, [switch]$update = $false, [string]$log = ($env:TEMP + "\ESXi-Customizer-PS.log") ) # Constants $ScriptName = "ESXi-Customizer-PS" $ScriptVersion = "2.3" $ScriptURL = "http://ESXi-Customizer-PS.v-front.de" $AccLevel = @{"VMwareCertified" = 1; "VMwareAccepted" = 2; "PartnerSupported" = 3; "CommunitySupported" = 4} # Online Depot URLs $vmwDepotURL = "https://hostupdate.vmware.com/software/VUM/PRODUCTION/main/vmw-depot-index.xml" $vftDepotURL = "http://vibsdepot.v-front.de/" # Function to update/add VIB package function AddVIB2Profile($vib) { $AddVersion = $vib.Version $ExVersion = ($MyProfile.VibList | where { $_.Name -eq $vib.Name }).Version if ($AccLevel[$vib.AcceptanceLevel.ToString()] -gt $AccLevel[$MyProfile.AcceptanceLevel.ToString()]) { write-host -ForegroundColor Yellow -nonewline (" [New AcceptanceLevel: " + $vib.AcceptanceLevel + "]") $MyProfile.AcceptanceLevel = $vib.AcceptanceLevel } If ($MyProfile.VibList -contains $vib) { write-host -ForegroundColor Yellow " [IGNORED, already added]" } else { Add-EsxSoftwarePackage -SoftwarePackage $vib -ImageProfile $MyProfile -force -ErrorAction SilentlyContinue | Out-Null if ($?) { if ($ExVersion -eq $null) { write-host -ForegroundColor Green " [OK, added]" } else { write-host -ForegroundColor Yellow (" [OK, replaced " + $ExVersion + "]") } } else { write-host -ForegroundColor Red " [FAILED, invalid package?]" } } } # Function to test if entered string is numeric function isNumeric ($x) { $x2 = 0 $isNum = [System.Int32]::TryParse($x, [ref]$x2) return $isNum } # Clean-up function function cleanup() { try { Remove-EsxSoftwareDepot $DefaultSoftwareDepots } catch {} Stop-Transcript | Out-Null } # Set up the screen $pswindow = (get-host).ui.rawui $newsize = $pswindow.buffersize if ( $newsize.height -lt 3000) { $newsize.height = 3000 } if ( $newsize.width -lt 120) { $newsize.width = 120 } $pswindow.buffersize = $newsize $newsize = $pswindow.windowsize if ( $newsize.height -lt 50) { $newsize.height = 50 } if ( $newsize.width -lt 120) { $newsize.width = 120 } $pswindow.windowsize = $newsize $pswindow.windowtitle = $ScriptName + " " + $ScriptVersion + " - " + $ScriptUrl $pswindow.foregroundcolor = "White" $pswindow.backgroundcolor = "Black" # Write info and help if requested write-host "`nScript to build a customized ESXi installation ISO or Offline bundle using the VMware PowerCLI ImageBuilder snapin" if ($help) { write-host "`nUsage:" write-host " ESXi-Customizer-PS [-help] | [-izip [-update]] [-sip] [-v50|-v51|-v55] [-ozip] [-pkgDir ]" write-host " [-outDir ] [-vft] [-dpt depot1[,...]] [-load vib1[,...]] [-log ]" write-host " [-ipname ] [-ipdesc ] [-ipvendor ] [-nsc] [-test]" write-host "`nOptional parameters:" write-host " -help : display this help" write-host " -izip : use the VMware Offline bundle as input instead of the Online depot" write-host " -update : only with -izip, updates a local bundle with an ESXi patch from the VMware Online Depot" write-host " -pkgDir : local directory of Offline bundles and/or VIB files to add (if any, no default)" write-host " -obDir : [DEPRECATED] a synonym for -pkgDir, use -pkgDir instead" write-host " -ozip : output an Offline bundle instead of an installation ISO" write-host " -outDir : directory to store the customized ISO or Offline bundle" write-host " (the default is the script directory)" write-host " -vft : connect the V-Front Online Depot" write-host " -dpt depot1[,...] : connect additional Online Depots by URL" write-host " -load vib1[,...] : load additional packages from connected Online Depots" write-host " -sip : select an ImageProfile from the current list" write-host " (default = auto-select latest available standard profile)" write-host " -v50 | -v51 | -v55 : Use only ESXi 5.0/5.1/5.5 profiles as input, ignore other versions" write-host " -nsc : use -NoSignatureCheck with export" write-host " -log : write log to (default is %TEMP%\ESXi-Customizer-PS.log)" write-host " -ipname " write-host " -ipdesc " write-host " -ipvendor : provide a name, description and/or vendor for the customized" write-host " ImageProfile (the default is derived from the cloned input ImageProfile)" write-host " -test : skip package download and image build (for testing)`n" exit } else { write-host "(Call with -help for instructions)" write-host ("`nLogging to " + $log + " ...") # Stop active transcript try { Stop-Transcript | out-null } catch {} # Start own transcript try { Start-Transcript -Path $log -Force -Confirm:$false | Out-Null } catch { write-host -ForegroundColor Red "`nFATAL ERROR: Log file cannot be opened. Bad file path or missing permission?`n" exit } } try { # Load the PowerCLI Core Snapin (if not already loaded) if (!(Get-PSSnapin -name VMware.VimAutomation.Core -ErrorAction:SilentlyContinue)) { if (!(Add-PSSnapin -PassThru VMware.VimAutomation.Core)) { # Error out if loading fails write-host -ForegroundColor Red "`nFATAL ERROR: Cannot load the PowerCLI Core Snapin. Is PowerCLI installed?`n" exit } } # Parameter sanity check if ( ($v50 -and ($v51 -or $v55 -or $v60)) -or ($v51 -and ($v55 -or $v60)) -or ($v55 -and $v60) ) { write-host -ForegroundColor Yellow "`nWARNING: Multiple ESXi versions specified. Highest version will take precedence!" } if ($update -and ($izip -eq "")) { write-host -ForegroundColor Red "`nFATAL ERROR: -update requires -izip!`n" exit } if ($obDir -ne "") { write-host -ForegroundColor Yellow "`nWARNING: -obDir is DEPRECATED, please use -pkgDir instead!" if ($pkgDir -eq "") { $pkgDir = $obDir } else { write-host -ForegroundColor Yellow "WARNING: Both -pkgDir and -obDir specified. -obDir will be ignored!" } } # Check PowerShell and PowerCLI version if (!(Test-Path variable:PSVersionTable)) { write-host -ForegroundColor Red "`nFATAL ERROR: This script requires at least PowerShell version 2.0!`n" exit } $psv = $PSVersionTable.PSVersion | select Major,Minor $pcv = Get-PowerCLIVersion | select major,minor,UserFriendlyVersion write-host ("`nRunning with PowerShell version " + $psv.Major + "." + $psv.Minor + " and " + $pcv.UserFriendlyVersion) if ( ($pcv.major -lt 5) -or (($pcv.major -eq 5) -and ($pcv.minor -eq 0)) ) { write-host -ForegroundColor Red "`nFATAL ERROR: This script requires at least PowerCLI version 5.1 !`n" exit } # Load the ImageBuilder Snapin (if not already loaded) if (!(Get-PSSnapin -name VMware.ImageBuilder -ErrorAction:SilentlyContinue)) { if (!(Add-PSSnapin -PassThru VMware.ImageBuilder)) { # Error out if loading fails write-host -ForegroundColor Red "`nFATAL ERROR: Cannot load the ImageBuilder Snapin. Please re-install the latest PowerCLI!`n" exit } } if ($update) { # Try to add Offline bundle specified by -izip write-host -nonewline "`nAdding Base Offline bundle $izip (to be updated)..." if ($updDepot = Add-EsxSoftwareDepot $izip) { write-host -ForegroundColor Green " [OK]" } else { write-host -ForegroundColor Red "`nFATAL ERROR: Cannot add Base Offline bundle!`n" exit } if (!($CloneIP = Get-EsxImageProfile -SoftwareDepot $updDepot)) { write-host -ForegroundColor Red "`nFATAL ERROR: No ImageProfiles found in Base Offline Bundle!`n" exit } } if (($izip -eq "") -or $update) { # Connect the VMware ESXi base depot write-host -nonewline "`nConnecting the VMware ESXi base depot ..." if ($baseDepot = Add-EsxSoftwareDepot $vmwDepotURL) { write-host -ForegroundColor Green " [OK]" } else { write-host -ForegroundColor Red "`nFATAL ERROR: Cannot add VMware base Online depot. Please check your internet connectivity and/or proxy settings!`n" exit } } else { # Try to add Offline bundle specified by -izip write-host -nonewline "`nAdding base Offline bundle $izip ..." if ($baseDepot = Add-EsxSoftwareDepot $izip) { write-host -ForegroundColor Green " [OK]" } else { write-host -ForegroundColor Red "`nFATAL ERROR: Cannot add VMware base Offline bundle!`n" exit } } if ($vft) { # Connect the V-Front Online Depot write-host -nonewline "`nConnecting the V-Front Online Depot ..." if ($vftDepot = Add-EsxSoftwareDepot $vftDepotURL) { write-host -ForegroundColor Green " [OK]" } else { write-host -ForegroundColor Red "`nFATAL ERROR: Cannot add the V-Front Online depot. Please check your internet connectivity and/or proxy settings!`n" exit } } if ($dpt -ne @()) { # Connect additional depots $AddDpt = @() for ($i=0; $i -lt $dpt.Length; $i++ ) { write-host -nonewline ("`nConnecting Online Depot " + $dpt[$i] + " ...") if ($AddDpt += Add-EsxSoftwareDepot $dpt[$i]) { write-host -ForegroundColor Green " [OK]" } else { write-host -ForegroundColor Red "`nFATAL ERROR: Cannot add Online depot. Please check your internet connectivity and/or proxy settings!`n" exit } } } write-host -NoNewLine "`nGetting ImageProfiles, please wait ..." $iplist = @() if ($v60) { Get-EsxImageProfile "ESXi-6.0*" -SoftwareDepot $baseDepot | foreach { $iplist += $_ } } else { if ($v55) { Get-EsxImageProfile "ESXi-5.5*" -SoftwareDepot $baseDepot | foreach { $iplist += $_ } } else { if ($v51) { Get-EsxImageProfile "ESXi-5.1*" -SoftwareDepot $baseDepot | foreach { $iplist += $_ } } else { if ($v50) { Get-EsxImageProfile "ESXi-5.0*" -SoftwareDepot $baseDepot | foreach { $iplist += $_ } } else { # Workaround for http://kb.vmware.com/kb/2089217 Get-EsxImageProfile "ESXi-5.0*" -SoftwareDepot $baseDepot | foreach { $iplist += $_ } Get-EsxImageProfile "ESXi-5.1*" -SoftwareDepot $baseDepot | foreach { $iplist += $_ } Get-EsxImageProfile "ESXi-5.5*" -SoftwareDepot $baseDepot | foreach { $iplist += $_ } Get-EsxImageProfile "ESXi-6.0*" -SoftwareDepot $baseDepot | foreach { $iplist += $_ } } } } } if ($iplist.Length -eq 0) { write-host -ForegroundColor Red " [FAILED]`n`nFATAL ERROR: No valid ImageProfile(s) found!" if ($iZip) { write-host -ForegroundColor Red "The input file is probably not a full ESXi base bundle.`n" } exit } else { write-host -ForegroundColor Green " [OK]" $iplist = @( $iplist | Sort-Object -Descending -Property @{Expression={$_.Name.Substring(0,10)}},@{Expression={$_.CreationTime.Date}},Name ) } # if -sip then display menu of available image profiles ... if ($sip) { if ($update) { write-host "`nSelect ImageProfile to use for update:" } else { write-host "`nSelect Base ImageProfile:" } write-host "-------------------------------------------" for ($i=0; $i -lt $iplist.Length; $i++ ) { write-host ($i+1): $iplist[$i].Name } write-host "-------------------------------------------" do { $sel = read-host "Enter selection" if (isNumeric $sel) { if (([int]$sel -lt 1) -or ([int]$sel -gt $iplist.Length)) { $sel = $null } } else { $sel = $null } } until ($sel) $idx = [int]$sel-1 } else { $idx = 0 } if ($update) { $updIP = $iplist[$idx] } else { $CloneIP = $iplist[$idx] } write-host ("`nUsing ImageProfile " + $CloneIP.Name + " ...") write-host ("(dated " + $CloneIP.CreationTime + ", AcceptanceLevel: " + $CloneIP.AcceptanceLevel + ",") write-host ($CloneIP.Description + ")") # If customization is required ... if (($pkgDir -ne "") -or $update -or ($load -ne @())) { # Create your own Imageprofile if ($ipname -eq "") { $ipname = $CloneIP.Name + "-customized" } if ($ipvendor -eq "") { $ipvendor = $CloneIP.Vendor } if ($ipdesc -eq "") { $ipdesc = $CloneIP.Description + " (customized)" } $MyProfile = New-EsxImageProfile -CloneProfile $CloneIP -Vendor $ipvendor -Name $ipname -Description $ipdesc # Update from Online depot profile if ($update) { write-host ("`nUpdating with the VMware ImageProfile " + $UpdIP.Name + " ...") write-host ("(dated " + $UpdIP.CreationTime + ", AcceptanceLevel: " + $UpdIP.AcceptanceLevel + ",") write-host ($UpdIP.Description + ")") $diff = Compare-EsxImageProfile $MyProfile $UpdIP $diff.UpgradeFromRef | foreach { $uguid = $_ $uvib = Get-EsxSoftwarePackage | where { $_.Guid -eq $uguid } write-host -nonewline " Add VIB" $uvib.Name $uvib.Version AddVIB2Profile $uvib } } # Loop over Offline bundles and VIB files if ($pkgDir -ne "") { write-host "`nLoading Offline bundles and VIB files from" $pkgDir ... foreach ($oBundle in Get-Item $pkgDir\*.zip) { write-host -nonewline " Loading" $oBundle ... if ($ob = Add-EsxSoftwareDepot $oBundle -ErrorAction SilentlyContinue) { write-host -ForegroundColor Green " [OK]" $ob | Get-EsxSoftwarePackage | foreach { write-host -nonewline " Add VIB" $_.Name $_.Version AddVIB2Profile $_ } } else { write-host -ForegroundColor Red " [FAILED]`n Probably not a valid Offline bundle, ignoring." } } foreach ($vibFile in Get-Item $pkgDir\*.vib) { write-host -nonewline " Loading" $vibFile ... try { $vib1 = Get-EsxSoftwarePackage -PackageUrl $vibFile -ErrorAction SilentlyContinue write-host -ForegroundColor Green " [OK]" write-host -nonewline " Add VIB" $vib1.Name $vib1.Version AddVIB2Profile $vib1 } catch { write-host -ForegroundColor Red " [FAILED]`n Probably not a valid VIB file, ignoring." } } } # Load additional packages from Online Depots if ($load -ne @()) { write-host "`nLoad additional VIBs from Online Depots ..." for ($i=0; $i -lt $load.Length; $i++ ) { if ($ovib = Get-ESXSoftwarePackage $load[$i] -Newest) { write-host -nonewline " Add VIB" $ovib.Name $ovib.Version AddVIB2Profile $ovib } else { write-host -ForegroundColor Red " [ERROR] Cannot find VIB named" $load[$i] "!" } } } } else { $MyProfile = $CloneIP } # Build the export command: $cmd = "Export-EsxImageProfile -ImageProfile " + "`'" + $MyProfile.Name + "`'" if ($ozip) { $outFile = "`'" + $outDir + "\" + $MyProfile.Name + ".zip" + "`'" $cmd = $cmd + " -ExportToBundle" } else { $outFile = "`'" + $outDir + "\" + $MyProfile.Name + ".iso" + "`'" $cmd = $cmd + " -ExportToISO" } $cmd = $cmd + " -FilePath " + $outFile if ($nsc) { $cmd = $cmd + " -NoSignatureCheck" } $cmd = $cmd + " -Force" # Run the export: write-host -nonewline ("`nExporting the ImageProfile to " + $outFile + ". Please be patient ...") if ($test) { write-host -ForegroundColor Yellow " [Skipped]" } else { write-host "`n" Invoke-Expression $cmd } write-host -ForegroundColor Green "`nAll done.`n" } catch { write-host -ForegroundColor Red ("`n`nAn unexpected error occured:`n" + $Error[0]) write-host -ForegroundColor Red ("`nIf requesting support please be sure to include the log file`n " + $log + "`n`n") } finally { cleanup } # SIG # Begin signature block # MIIcTQYJKoZIhvcNAQcCoIIcPjCCHDoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUjuvJg3Fc9RDVOY8myotSXPp+ # 4UegghZzMIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B # AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG # A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh # d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg # Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV # UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu # dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q # WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC # i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4 # ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3 # +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI # fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd # BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG # CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB # Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro # YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV # HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y # MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf # plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y # 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq # IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3 # DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh # dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD # QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE # BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT # eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow # mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0 # jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu # ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh # d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz # C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB # o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO # BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw # Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90 # cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx # oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy # bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV # HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa # 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH # bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73 # BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR # EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW # yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu # e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw # ggZwMIIEWKADAgECAgEkMA0GCSqGSIb3DQEBBQUAMH0xCzAJBgNVBAYTAklMMRYw # FAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQLEyJTZWN1cmUgRGlnaXRhbCBD # ZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBTdGFydENvbSBDZXJ0aWZpY2F0 # aW9uIEF1dGhvcml0eTAeFw0wNzEwMjQyMjAxNDZaFw0xNzEwMjQyMjAxNDZaMIGM # MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi # U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3Rh # cnRDb20gQ2xhc3MgMiBQcmltYXJ5IEludGVybWVkaWF0ZSBPYmplY3QgQ0EwggEi # MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKI4siNR6aoBs8nUnQPwyXOBYp # uvh9iVtFWO+EcO1+EU3pFDGrQ+NNDFGBbPAVA0okJ1Tl+0qgzk3hhKMh3pk1q9xJ # rr8xxWeEMBCb7wfcdagPTfQ1U7FuOAP8iHcdpXf/P3Xn2ee/LFARyRFl+kkHYp+T # poepbcmdK9F75dVlK58NUJ7++3EZITAoJo2uwtz2luhShggLejLNahRNnrn5zQfi # lpHxzx4r+YL3XiYGjo3R1DnXb9uRJ1p5j1hpCka1b+H9b8WRtBFPewKm20tWUiOe # S5jiv37O+qFOg+PFx8NgR/5cPxUaQCqV7wBryFD4zWoZ1CMDJ7w7NtW5Q7DvAgMB # AAGjggHpMIIB5TAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV # HQ4EFgQU0E4PQJlsuEsZbzsouODjiAc0qrcwHwYDVR0jBBgwFoAUTgvvGqRAW6UX # aYcwyjRoQ9BBrvIwPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzAChiFodHRwOi8v # d3d3LnN0YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0 # cDovL3d3dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3Js # LnN0YXJ0c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcB # AgEwZjAuBggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5 # LnBkZjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJt # ZWRpYXRlLnBkZjARBglghkgBhvhCAQEEBAMCAAEwUAYJYIZIAYb4QgENBEMWQVN0 # YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgT2JqZWN0IFNpZ25p # bmcgQ2VydGlmaWNhdGVzMA0GCSqGSIb3DQEBBQUAA4ICAQBycwsDdVo3g4gT2XhB # Pk4S1nLk8HIGK3egeKpCmBURCjsMdGyNcPkf8jJOK+kyKRpp5HEi/3ltpF3iGhRw # zAOPgkiMLdYD0Wg0VXfVIyWMRlrrobxFAQJ0xJK5+B8Ni7VdD5xQrGEPcS0sYZwU # aOMwvsRC/YiiXvjWsSzJxfAhdyvLF6IxtTZM+Ltfd6VvBAxzgkWUngHL0WEHO5kH # UNXaw3aKsZVsLcb/X5LZ2g8OMvUJoSXBFr9PSqSra+8/FSCvICgKmlQUpWLDnKgZ # gL7PUZp6xZaI/V4UoAvTAjsiBK8vNTfLVWnu+xhrE5UGpm15sVNZEe1eMKwWutAG # eC3R3fdBtBEjmbCDMSntcn3G7l3pFVYzhM9FSx34MNmkEeb2azO+L2BUVvZkbupF # FcJKrKzj6780sE9teL+b+VTTRw4NBOUL967COT0dC1GtdD/OqwElLpQn54sbDWo5 # +P4dUGX9lCl+guTsihaVFC9EvWzuiKsRqo9lQhZj+Cter2vqMMoCnctl0pCk86ee # iC2qVTh/v+QuMQmGutz3yas5aZUwr8G4VEB9DmgNQydWYLMDMsyMp8ZxVb+Ix7Dj # XJ+GApvCl/ObcsGvVm/6kQGByBbqidEtICfdcczR423P4CTEfqtF/oHaZiEsQQYt # qkfxHUAwCjgFtUU5lHmRdwwLCjCCB2IwggZKoAMCAQICAgrxMA0GCSqGSIb3DQEB # BQUAMIGMMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkG # A1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UE # AxMvU3RhcnRDb20gQ2xhc3MgMiBQcmltYXJ5IEludGVybWVkaWF0ZSBPYmplY3Qg # Q0EwHhcNMTMwOTA3MDY1MzQ2WhcNMTUwOTA4MDY1NjI4WjCBjjEZMBcGA1UEDRMQ # ZXZtMThUZ2luSDVFNGR0WjELMAkGA1UEBhMCREUxDzANBgNVBAgTBkhlc3NlbjES # MBAGA1UEBxMJRnJhbmtmdXJ0MRYwFAYDVQQDEw1BbmRyZWFzIFBlZXR6MScwJQYJ # KoZIhvcNAQkBFhhzdGFydGNvbUBwZWV0ei1vbmxpbmUuZGUwggIiMA0GCSqGSIb3 # DQEBAQUAA4ICDwAwggIKAoICAQCvxJJoiKPibd6ZMCamADNbJRSrx7V1HtclCuZ+ # E/7u6XHVALCE6heyw1T4c3irN6BCVQVWIoRf7nwVDGhO2AwHGClL5QWx/DE5wSoV # EZfuGbwNRG0dRnuyQYGau+3yrKt6/BdKd7k18NmsIAfvIhQpbrxUXP0IZJM2/EPl # IjxtDM88gN50XQ8hfccDpJHSRrsKDAqvVsvRsWl59VVwnFeIBGnCcbKQ22OA34Ht # 7r4kWWGx4ePpbcfSQJF6P7LMJZYsfAGzOcdyQ0RuLMvG8uK+zE4iMyC5Tr1WrjbM # pgz1cZR7lX2Lz6Kx+BbkfgD7p8o9Zp9mgc/IVgKyRniUZWWazAZD33NAimjLKU49 # b1gix60nPvUg1mZz+bQVV97M73RtFDk03wg+DG3ULMa5gmymxYRvLPqW7B5si1w2 # 44kqPz69Ia9IxpGD19mOEG34J9UP97Gjf6wFSigG4vnddG7/wolvlJY9nsp7VvBr # 2A9QX9RsjjS31bJq4ofFk7tw3OYuN/0dXZbYRQFMmYut1F6Kz7DGhwOEMMH+IRqa # YMiG8m8IeQPsrjrJVGIOor8CLvs662KifVjwseydq2UNDhoLXH/lZp6VAo500uYp # gA+wsL55lpjlbEd2WpLV9LmYnjzIWe7jS3ycZUPuJxEcioB1mMxIqruHx1p6OA2B # bQqk2wIDAQABo4ICyDCCAsQwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwLgYD # VR0lAQH/BCQwIgYIKwYBBQUHAwMGCisGAQQBgjcCARUGCisGAQQBgjcKAw0wHQYD # VR0OBBYEFMPgr45I1kRyvJ3R4NkC4vBUpC2XMB8GA1UdIwQYMBaAFNBOD0CZbLhL # GW87KLjg44gHNKq3MIIBTAYDVR0gBIIBQzCCAT8wggE7BgsrBgEEAYG1NwECAzCC # ASowLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w # ZGYwgfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0 # aG9yaXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3Jk # aW5nIHRvIHRoZSBDbGFzcyAyIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRo # ZSBTdGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRl # bmRlZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkg # b2JsaWdhdGlvbnMuMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuc3RhcnRz # c2wuY29tL2NydGMyLWNybC5jcmwwgYkGCCsGAQUFBwEBBH0wezA3BggrBgEFBQcw # AYYraHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9jbGFzczIvY29kZS9jYTBA # BggrBgEFBQcwAoY0aHR0cDovL2FpYS5zdGFydHNzbC5jb20vY2VydHMvc3ViLmNs # YXNzMi5jb2RlLmNhLmNydDAjBgNVHRIEHDAahhhodHRwOi8vd3d3LnN0YXJ0c3Ns # LmNvbS8wDQYJKoZIhvcNAQEFBQADggEBAGc14LGFqvGjqkv5yCGlnM07FYX+UNNS # 1Mtd61WDjsuqQgz8bUuCY4XSm2bVnI7TZqB+cgOsu51nWerYl4kNQpmbDvWFeCbL # INE0Dh7eW968ez6/BEac1Cm8dQ5s9MEU4G3euWVIOtfr7JJ9ypzG+WY6+OGTAXzE # ipBEf5kIQFRgI3yW4FYJBQRh8EK5J4ln5bsL0dehpVoH3lZB+lP/plgiONUhmAGO # Tm4I7JMeYXvmtXZxuMeHn/YtRiNTYIztUIJDouFjBrERZpX5mQa/nYyTKWJ7ps/5 # Y7cBvqqDfSrBSWv8J0swxbMKyaAlEqpWBnkKah5HpN6m/otX+vCXFU0xggVEMIIF # QAIBATCBkzCBjDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4x # KzApBgNVBAsTIlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2 # BgNVBAMTL1N0YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgT2Jq # ZWN0IENBAgIK8TAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKA # ADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYK # KwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUlypZp4/7SwTiQm81E9zYlS7Z2K8w # DQYJKoZIhvcNAQEBBQAEggIAVv1Vj0wVlPxVKCrRU0MVexZuQ7zfz1U79O06ll/V # sbFW4L07emE3iq/RNp/obHpmE6Qo8RX4OjLK7MIgQ4tZy7+StJG0C+o2SDZ9Sekd # ueU4RCE1KtFUnjat40VgthvXxRxgo2PSeR62swDOvvhL3Pa3aRY2U/W+2KuKRKd+ # xh66zfK/+UrVadkoSE1+0vqjHbWZqLSwCgVgfTsHny57Qgnh3Qcw8eXbMHgpLP7G # 3wrrntLim41t4i84vSkYrl0ZNlnHkd7zdo6j10r2VRekLSzD2CIHwkXqff0KiKc3 # zCMSzIghZeUacu/ZHGb02WH1wIG51HcDtLJbNyh8m0yZyPKxN3HUbNIegS/X4Dtw # S2ioJaqU1fradk8ZXEGw37cTW3GCGgVERJlJqqofoyNkJEjI6KGpuGwj+E4Sa5uL # zVjU4VYH/mKGtSp8WhLgJB0y6zofnM8M26oNnq26DKtex/w8RZxrdyA0bXMBriwY # 8A3M34c1vlbse9OLCLAcw3D6TUVsgYvQqkIAKRvthkUOz+7lc3aBClSPlkuQtZKW # Ae5rN3ITtjuZMogihb1mmp7ai5P/e8oL1+RWl9900EY+BViGceyKnJUdBel4YjKq # pd3kXDCLpcZu2DJ0fghPQeXzxM24Mb77yKMUxNixLxKcq81wQFBs1baxYKwxkaPo # hlShggILMIICBwYJKoZIhvcNAQkGMYIB+DCCAfQCAQEwcjBeMQswCQYDVQQGEwJV # UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu # dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMgIQDs/0OMj+vzVuBNhq # mBsaUDAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkq # hkiG9w0BCQUxDxcNMTQxMDAxMDc1MjI2WjAjBgkqhkiG9w0BCQQxFgQUCJwFuCmW # lTs3lulIc95jZI6cV+4wDQYJKoZIhvcNAQEBBQAEggEAhoqqCqVIwYYaFu0Zn5da # //y59goarKpozrNb21G+utbt4db4cxAHMfrK59wkegdLW8ksh8txt3wHnJjptgVM # RRX3qJCgMH+Yw1iACKuO5OkuHRntcfk08oDWFBQJTE3D8Ult5og/unMg+GB269L5 # +BwCpDYlnvVpu88VULfNFutoSLCMJ+ZJWtrDNfxG22qbjgnRZRktTeCUt9ZIn0xk # wI7w5mUoQmhtTYoNh4QBGXs7dS6TZsmX8NtpkHIBXF3qAYCQDXeBVlB1YkbsUjzM # QUPnf8iTBFL5iAqJc921Gl1ugvO6b/+EUMAf3kGlgCc2f6PIhxwE1eGf5SbiYrKh # Zw== # SIG # End signature block