ozo-ad-lab-implement-customization-prerequisites.ps1
#Requires -Modules @{ModuleName="OZO";ModuleVersion="1.5.1"},@{ModuleName="OZOLogger";ModuleVersion="1.1.0"} -RunAsAdministrator <#PSScriptInfo .VERSION 0.1.0 .GUID 2a8769c1-6be2-44f3-ae17-47b4138ea2fa .AUTHOR Andy Lievertz <alievertz@onezeroone.dev> .COMPANYNAME One Zero One .COPYRIGHT This script is released under the terms of the GNU General Public License ("GPL") version 2.0. .TAGS .LICENSEURI https://github.com/onezeroone-dev/OZO-AD-Lab-Implement-Prerequisites/blob/main/LICENSE .PROJECTURI https://github.com/onezeroone-dev/OZO-AD-Lab-Implement-Prerequisites .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES https://github.com/onezeroone-dev/OZO-AD-Lab-Implement-Prerequisites/blob/main/CHANGELOG.md .PRIVATEDATA #> <# .SYNOPSIS See description. .DESCRIPTION Implements the prerequisites for the One Zero One AD Lab. .EXAMPLE ozo-ad-lab-implement-prerequisites .LINK https://github.com/onezeroone-dev/OZO-AD-Lab-Implement-Prerequisites/blob/main/README.md #> Class ADLIP { # PROPERTIES: Booleans, Strings [Boolean] $Relog = $true [String] $currentUser = $null [String] $downloadsDir = $null [String] $featureName = $null [String] $gitExePath = $null [String] $localGroup = $null [String] $ozoAdLabDirLike = $null [String] $ozoAdLabPath = $null [String] $ozoAdLabZipPath = $null [String] $ozoAdLabZipUri = $null [String] $winAdkFileName = $null [String] $winAdkPath = $null [String] $winAdkFileUri = $null [String] $wingetExePath = $null # PROPERTIES: PSCustomObjects [PSCustomObject] $ozoLogger = @() # PROPERTIES: Lists [System.Collections.Generic.List[PSCustomObject]] $ozoADLabISOs = @() # METHODS # Constructor method ADLIP() { # Set properties $this.currentUser = ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name) $this.downloadsDir = (Join-Path -Path $Env:USERPROFILE -ChildPath "Downloads") $this.featureName = "Microsoft-Hyper-V-All" $this.localGroup = "Hyper-V Administrators" $this.ozoAdLabDirLike = "onezeroone-dev-OZO-AD-Lab*" $this.ozoAdLabPath = (Join-Path -Path $Env:SystemDrive -ChildPath "ozo-ad-lab") $this.ozoAdLabZipPath = (Join-Path -Path $Env:USERPROFILE -ChildPath "Downloads\ozo-ad-lab-latest.zip") $this.ozoAdLabZipUri = "https://api.github.com/repos/onezeroone-dev/OZO-AD-Lab/releases/latest" $this.winAdkFileName = "adksetup.exe" $this.winAdkPath = (Join-Path -Path $this.downloadsDir -Childpath $this.winAdkFileName) $this.winAdkFileUri = "https://download.microsoft.com/download/2/d/9/2d9c8902-3fcd-48a6-a22a-432b08bed61e/ADK/adksetup.exe" $this.wingetExePath = (Join-Path -Path $Env:LOCALAPPDATA -ChildPath "Microsoft\WindowsApps\winget.exe") # Populate the ozoADLabISOs list $this.ozoADLabISOs.Add([PSCustomObject]@{Name="almalinux-boot.iso";Uri="https://repo.almalinux.org/almalinux/9.5/isos/x86_64/AlmaLinux-9.5-x86_64-boot.iso"}) $this.ozoADLabISOs.Add([PSCustomObject]@{Name="microsoft-windows-11-enterprise-evaluation.iso";Uri="https://software-static.download.prss.microsoft.com/dbazure/888969d5-f34g-4e03-ac9d-1f9786c66749/26100.1742.240906-0331.ge_release_svc_refresh_CLIENTENTERPRISEEVAL_OEMRET_x64FRE_en-us.iso"}) $this.ozoADLabISOs.Add([PSCustomObject]@{Name="microsoft-windows-11-laof.iso";Uri="https://software-static.download.prss.microsoft.com/dbazure/888969d5-f34g-4e03-ac9d-1f9786c66749/26100.1.240331-1435.ge_release_amd64fre_CLIENT_LOF_PACKAGES_OEM.iso"}) $this.ozoADLabISOs.Add([PSCustomObject]@{Name="microsoft-windows-server-2022-evaluation.iso";Uri="https://software-static.download.prss.microsoft.com/sg/download/888969d5-f34g-4e03-ac9d-1f9786c66749/SERVER_EVAL_x64FRE_en-us.iso"}) # Create a logger object $this.ozoLogger = (New-OZOLogger) # Declare ourselves to the world $this.ozoLogger.Write("Process starting.","Information") # Call ValidateEnvironment to determine if we can proceed If ($this.ValidateEnvironment() -eq $true) { # Environment validates; report and call ProcessPrerequisites to ...process the prerequisites $this.ozoLogger.Write("Environment validates.","Information") $this.ProcessPrerequisites() } Else { # Environment did not validate $this.ozoLogger.Write("The environment did not validate.","Error") } # Bid adieu to the world $this.ozoLogger.Write("Process complete.","Information") } # Process prerequisites method Hidden [Void] ProcessPrerequisites() { # Environment validates; install Hyper-V features $this.ozoLogger.Write("Installing Hyper-V features.","Information") If ($this.InstallHyperV() -eq $true) { # Hyper-V features are installed; install the Debian WSL distribution If ($this.InstallWSLDebian() -eq $true) { # WSL Debian distribution is installed; determine if a reboot is not required $this.ozoLogger.Write("Determining if a restart is required.","Information") If ($this.RestartRequired() -eq $false) { # Restart is not required; add the local user to the Hyper-V Administrators group $this.ozoLogger.Write("Adding user to the local Hyper-V Administrators group.","Information") If ($this.ManageLocalHyperVAdministratorsGroup() -eq $true) { # Local user is added to the local Hyper-V Administrators group; create the VM switches $this.ozoLogger.Write("Creating the Hyper-V VMSwitches.","Information") If ($this.CreateVMSwitches() -eq $true) { # VM switches are created; installed the Microsoft SDK $this.ozoLogger.Write("Installing the Microsoft ADK (Deployment Tools).","Information") If ($this.InstallMicrosoftADK() -eq $true) { # Microsoft SDK is installed; install Git for Windows $this.ozoLogger.Write("Downloading and extracting the latest release of the OZO AD Lab resources.","Information") If ($this.GetADLabResources() -eq $true) { # Got AD Lab resources; download the ISOs $this.ozoLogger.Write("Downloading the source ISOs (this could take some time).","Information") If ($this.DownloadISOs() -eq $true) { # ISOs are downloaded; report all prerequisites satisfied $this.ozoLogger.Write("All prerequistes are satisified. Please see https://onezeroone.dev/active-directory-lab-customize-the-windows-installer-isos for the next steps.","Information") } Else { # Download error $this.ozoLogger.Write("Error downloading ISOs. Please manually download the required ISOs. Then see https://onezeroone.dev/active-directory-lab-customize-the-windows-installer-isos for the next steps.","Error") } } Else { # Unable to get AD Lab Resources $this.ozoLogger.Write("Error downloading and extracting the latest OZO AD Lab resources. Please manually download and extract the latest release and run this script again to continue. See https://onezeroone.dev/active-directory-lab-prerequisites for more information.","Error") } } Else { # Microsoft SDK installation error $this.ozoLogger.Write("Error attempting to download and install the Microsoft ADK. Please manually download and install and then run this script again to continue. See https://onezeroone.dev/active-directory-lab-prerequisites for more information.","Error") } } Else { # VMSwitch creation error $this.ozoLogger.Write("Error creating the VM switches. Please manually create these switches then run this script again to continue. See https://onezeroone.dev/active-directory-lab-prerequisites for more information.","Error") } } Else { # Error adding user to local Hyper-V Administrators group $this.ozoLogger.Write(("Failure adding user " + $this.currentUser + " to the " + $this.localGroup + " group. Please manually add this user to this group then run this script again to continue. See https://onezeroone.dev/active-directory-lab-prerequisites for more information."),"Error") } } Else { # Restart is required $this.ozoLogger.Write("Please restart to complete the feature installation and then run this script again to continue.","Warning") # Get restart decision If ((Get-OZOYesNo) -eq "y") { # User elects to restart Restart-Computer } } } Else { # Error installing WSL Debian $this.ozoLogger.Write(("Error installing the WSD Debian distribution. Please manually install this distribution and then run this script again to continue."),"Error") } } Else { # Error installing Hyper-V Feature $this.ozoLogger.Write(("Error installing the " + $this.featureName + " feature. Please manually install this feature and then run this script again to continue."),"Error") } } # Environment validation method Hidden [Boolean] ValidateEnvironment() { # Control variable [Boolean] $Return = $true # Determine if this a user-interactive session If ((Get-OZOUserInteractive) -eq $false) { # Session is not user-interactive $this.ozoLogger.Write("Please run this script in a user-interactive session.","Error") $Return = $false } # Determine if user is an Administrator If ((Test-OZOLocalAdministrator) -eq $false) { # User is not a local administrator $this.ozoLogger.Write("Please run this script in an Administrator PowerShell","Error") $Return =$false } # Determine of winget.exe does not exist If ((Test-Path -Path $this.wingetExePath) -eq $false) { # Did not find winget.exe $this.ozoLogger.Write("Missing winget.exe","Error") $Return = $false } # Determine if there is already an "ozo-ad-lab" folder off the root of the SystemDrive If ((Test-Path -Path $this.ozoAdLabPath) -eq $true) { # There is already an "ozo-ad-lab" folder $this.ozoLogger.Write(("Found " + $this.ozoAdLabPath + ". This directory must be removed before proceeding."),"Error") $Return = $false } # Make sure any previous downloaded + extracted releases of OZO-AD-Lab are wiped (Get-ChildItem -Path $Env:TEMP | Where-Object {$_.Name -Like $this.ozoAdLabDirLike}) | Remove-Item -Recurse -Force # Return return $Return } # Install Hyper-V method Hidden [Boolean] InstallHyperV() { # Control variable [Boolean] $Return = $true # Determine if the feature is present If ([Boolean](Get-WindowsOptionalFeature -Online -FeatureName $this.featureName) -eq $false) { # Feature is not present; try to install it Try { Enable-WindowsOptionalFeature -Online -FeatureName $this.featureName -ErrorAction Stop # Success } Catch { # Failure $Return = $false } } # Return return $Return } Hidden [Boolean] InstallWSLDebian() { # Control variable [Boolean] $Return = $true # Check if WSL is installed/available and can install Debian Try { & wsl -l --Debian # Success } Catch { # Failure $Return = $false } # Return return $Return } # Reboot required method Hidden [Boolean] RestartRequired() { # Control variable [Boolean] $Return = $false # Determine if feature is present If ((Get-WindowsOptionalFeature -Online -FeatureName $this.featureName).RestartRequired -eq "Required") { # Restart is required $this.Return = $true } # Return return $Return } # Manage local Hyper-V Administrators group membership Hidden [Boolean] ManageLocalHyperVAdministratorsGroup() { # Control variable [Boolean] $Return = $true # Determine if the current user is a member of the local Hyper-V Administrators group If ((Get-LocalGroupMember -Name $this.localGroup).Name -NotContains $this.currentUser) { # User is not in the local group; try to add them Try { Add-LocalGroupMember -Group "Hyper-V Administrators" -Member $this.currentUser # Success } Catch { # Failure $Return = $false } } # Return return $Return } # Create VM switches method Hidden [Boolean] CreateVMSwitches() { # Control variable [Boolean] $Return = $true [String] $externalAdapter = $null # Determine if the private switch already exists If ([Boolean](Get-VMSwitch -Name "AD Lab Private") -eq $false) { # Private switch does not exist; try to create it Try { New-VMSwitch -Name "AD Lab Private" -SwitchType Private -ErrorAction Stop # Success } Catch { # Failure $Return = $false } } # Determine if the external switch already exists If ([Boolean](Get-VMSwitch -Name "AD Lab External") -eq $false) { # External switch does not exist; call Get-NetAdapter to display available network connections Write-Host (Get-NetAdapter) # Prompt the user for the name of the external network connection until they correctly identify an adapter Do { $externalAdapter = (Read-Host "Above is the output of the Get-NetAdapter command. Type the Name of the network adapter that corresponds with your external network (Internet) connection") } Until ((Get-NetAdapter).Name -Contains $externalAdapter) # Try to create the external switch Try { New-VMSwitch -Name "AD Lab External" -NetAdapterName $externalAdapter -ErrorAction Stop # Success } Catch { # Failure $Return = $false } } # Return return $Return } # Install Microsoft SDK method Hidden [Boolean] InstallMicrosoftADK() { # Control variable [Boolean] $Return = $true # Local variables [String] $oscdimgExePath = (Join-Path -Path ${Env:ProgramFiles(x86)} -ChildPath "Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe") [String] $simExePath = (Join-Path -Path ${Env:ProgramFiles(x86)} -ChildPath "Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\WSIM\x86\imgmgr.exe") # Determine if oscdimg.exe is note present If ((Test-Path -Path $oscdimgExePath) -eq $false -Or (Test-Path $simExePath) -eq $false) { # Did not find oscdimg.exe; try to download and install Try { Invoke-WebRequest -Uri $this.winAdkFileUri -OutFile $this.winAdkPath -ErrorAction Stop # Success; try to install Try { Invoke-Command -ScriptBlock { & $this.winAdkPath /quiet /norestart /features OptionId.DeploymentTools } -ErrorAction Stop | Out-Null # Success; sleep until the installation is complete Do { Start-Sleep -Seconds 1 } Until ((Test-Path -Path $oscdimgExePath) -eq $true -And (Test-Path $simExePath) -eq $true) } Catch { # Failure $Return = $false } } Catch { # Failure; report $Return = $false } } # Return return $Return } # Get AD Lab resources method Hidden [Boolean] GetADLabResources() { # Control variable [Boolean] $Return = $true # Try to get the latest zipball Try { Invoke-WebRequest -Uri (Invoke-WebRequest -Uri $this.ozoAdLabZipUri -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop).zipball_url -OutFile $this.ozoAdLabZipPath -ErrorAction Stop # Success; expand the archive Expand-Archive -Path $this.ozoAdLabZipPath -DestinationPath $Env:TEMP -ErrorAction Stop # Remove the archive Remove-Item -Path $this.ozoAdLabZipPath -Force # Move the extracted folder to the ozoAdLab Move-Item -Path (Get-ChildItem -Path $Env:TEMP -ErrorAction Stop | Where-Object {$_.Name -Like $this.ozoAdLabDirLike} | Select-Object -First 1).FullName -Destination $this.ozoAdLabPath # Create required (empty) Mount subdirectory New-Item -ItemType Directory -Path (Join-Path -Path $this.ozoAdLabPath -ChildPath "Mount") -ErrorAction Stop } Catch { #Failure $Return = $false } # Return return $Return } # Download ISOs method Hidden [Boolean] DownloadISOs() { # Control variable [Boolean] $Return = $true # Iterate through the ISOs ForEach ($ozoAdLabIso in $this.ozoADLabISOs) { # Try to get the ISO Try { Invoke-WebRequest -Uri $ozoAdLabIso.Uri -OutFile (Join-Path -Path $this.ozoAdLabPath -ChildPath (Join-Path -Path "ISO" -ChildPath $ozoAdLabIso.Name)) -ErrorAction Stop # Success } Catch { # Failure $Return = $false } } # Return return $Return } } Function Get-OZOYesNo { # Prompt the user to restart and return the lowercase of the first letter of their response [String]$response = $null Do { $response = (Read-Host "(Y/N)")[0].ToLower() } Until ($response -eq "y" -Or $response -eq "n") # Return response return $response } Function New-WSLPathFromWindowsPath { param( [Parameter(Mandatory=$true,HelpMessage="The Windows path to convert")][String]$WindowsPath ) # Local variables return ($WindowsPath.Replace("\","/")).Replace($Env:SystemDrive,("/mnt/" + ($Env:SystemDrive.Split(":"))[0].ToLower())) } # MAIN [ADLIP]::new() | Out-Null # SIG # Begin signature block # MIIfcgYJKoZIhvcNAQcCoIIfYzCCH18CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC+OOTY7NFduyaX # QCCkPJ5JJKcJ6S7sqkHYwBZfOnTzV6CCDPgwggZyMIIEWqADAgECAghkM1HTxzif # CDANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx # EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8G # A1UEAwwoU1NMLmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTAe # Fw0xNjA2MjQyMDQ0MzBaFw0zMTA2MjQyMDQ0MzBaMHgxCzAJBgNVBAYTAlVTMQ4w # DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENv # cnAxNDAyBgNVBAMMK1NTTC5jb20gQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBD # QSBSU0EgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCfgxNzqrDG # bSHL24t6h3TQcdyOl3Ka5LuINLTdgAPGL0WkdJq/Hg9Q6p5tePOf+lEmqT2d0bKU # Vz77OYkbkStW72fL5gvjDjmMxjX0jD3dJekBrBdCfVgWQNz51ShEHZVkMGE6ZPKX # 13NMfXsjAm3zdetVPW+qLcSvvnSsXf5qtvzqXHnpD0OctVIFD+8+sbGP0EmtpuNC # GVQ/8y8Ooct8/hP5IznaJRy4PgBKOm8yMDdkHseudQfYVdIYyQ6KvKNc8HwKp4WB # wg6vj5lc02AlvINaaRwlE81y9eucgJvcLGfE3ckJmNVz68Qho+Uyjj4vUpjGYDdk # jLJvSlRyGMwnh/rNdaJjIUy1PWT9K6abVa8mTGC0uVz+q0O9rdATZlAfC9KJpv/X # gAbxwxECMzNhF/dWH44vO2jnFfF3VkopngPawismYTJboFblSSmNNqf1x1KiVgMg # Lzh4gL32Bq5BNMuURb2bx4kYHwu6/6muakCZE93vUN8BuvIE1tAx3zQ4XldbyDge # VtSsSKbt//m4wTvtwiS+RGCnd83VPZhZtEPqqmB9zcLlL/Hr9dQg1Zc0bl0EawUR # 0tOSjAknRO1PNTFGfnQZBWLsiePqI3CY5NEv1IoTGEaTZeVYc9NMPSd6Ij/D+KNV # t/nmh4LsRR7Fbjp8sU65q2j3m2PVkUG8qQIDAQABo4H7MIH4MA8GA1UdEwEB/wQF # MAMBAf8wHwYDVR0jBBgwFoAU3QQJB6L1en1SUxKSle44gCUNplkwMAYIKwYBBQUH # AQEEJDAiMCAGCCsGAQUFBzABhhRodHRwOi8vb2NzcHMuc3NsLmNvbTARBgNVHSAE # CjAIMAYGBFUdIAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwOwYDVR0fBDQwMjAwoC6g # LIYqaHR0cDovL2NybHMuc3NsLmNvbS9zc2wuY29tLXJzYS1Sb290Q0EuY3JsMB0G # A1UdDgQWBBRUwv4QlQCTzWr158DX2bJLuI8M4zAOBgNVHQ8BAf8EBAMCAYYwDQYJ # KoZIhvcNAQELBQADggIBAPUPJodwr5miyvXWyfCNZj05gtOII9iCv49UhCe204MH # 154niU2EjlTRIO5gQ9tXQjzHsJX2vszqoz2OTwbGK1mGf+tzG8rlQCbgPW/M9r1x # xs19DiBAOdYF0q+UCL9/wlG3K7V7gyHwY9rlnOFpLnUdTsthHvWlM98CnRXZ7WmT # V7pGRS6AvGW+5xI+3kf/kJwQrfZWsqTU+tb8LryXIbN2g9KR+gZQ0bGAKID+260P # Z+34fdzZcFt6umi1s0pmF4/n8OdX3Wn+vF7h1YyfE7uVmhX7eSuF1W0+Z0duGwdc # +1RFDxYRLhHDsLy1bhwzV5Qe/kI0Ro4xUE7bM1eV+jjk5hLbq1guRbfZIsr0WkdJ # LCjoT4xCPGRo6eZDrBmRqccTgl/8cQo3t51Qezxd96JSgjXktefTCm9r/o35pNfV # HUvnfWII+NnXrJlJ27WEQRQu9i5gl1NLmv7xiHp0up516eDap8nMLDt7TAp4z5T3 # NmC2gzyKVMtODWgqlBF1JhTqIDfM63kXdlV4cW3iSTgzN9vkbFnHI2LmvM4uVEv9 # XgMqyN0eS3FE0HU+MWJliymm7STheh2ENH+kF3y0rH0/NVjLw78a3Z9UVm1F5VPz # iIorMaPKPlDRADTsJwjDZ8Zc6Gi/zy4WZbg8Zv87spWrmo2dzJTw7XhQf+xkR6Od # MIIGfjCCBGagAwIBAgIQZ2iSsNbwOsjnLExSAX6F6DANBgkqhkiG9w0BAQsFADB4 # MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24x # ETAPBgNVBAoMCFNTTCBDb3JwMTQwMgYDVQQDDCtTU0wuY29tIENvZGUgU2lnbmlu # ZyBJbnRlcm1lZGlhdGUgQ0EgUlNBIFIxMB4XDTI0MTExNjEwMzUyOFoXDTI1MTEx # NjEwMzUyOFowZTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCENvbG9yYWRvMQ8wDQYD # VQQHDAZEZW52ZXIxGDAWBgNVBAoMD0FuZHJldyBMaWV2ZXJ0ejEYMBYGA1UEAwwP # QW5kcmV3IExpZXZlcnR6MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA # vIBAQzK0aahepOrPmvCEqfd6dMZC4GvV7kflKwrn4QPJGfqhFmUtadP1e3ange8O # QZ3/w7UjOTAUNUHfhjbSgUBlKjbS6EWQKZuRFzI3SNkMJkcjTX4uS2P4QsnwM+SW # IE5me3CTssdjtgue+Iiy53TMgW8JpoxiULVxmm3bhCRUAgxWeT6tzjytR1UyGcMc # cm/YE6TOgsCHiZoo4X4HJD9iHDrNldArq04Jl6FsADxEswttKyfqpIRJLoAysVl1 # f8CEDBwhszJrEXBnAlWViJFfNY+dKP4jhf7lLqSvPCuADqP2jvM0Ym5I8qDGMz9j # XPSMLF58MFB4vM4viS7nLRFJ8S1Q98vQvB8W4kk0WPuiZbZTHsROzohE1VSbLnIY # ag5dDOWI8L6yutAsfdZFYFmSTKcMSiOj5VbK4LhAJUL2G8vPwpTGFgr+cEp0p62F # P0WXK+/cRfGqodI5S+bg+9rQTD9zf829DwraSRAt5P5zrQk4WPst3JW/vIKNx7cV # AgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFFTC/hCVAJPN # avXnwNfZsku4jwzjMHoGCCsGAQUFBwEBBG4wbDBIBggrBgEFBQcwAoY8aHR0cDov # L2NlcnQuc3NsLmNvbS9TU0xjb20tU3ViQ0EtQ29kZVNpZ25pbmctUlNBLTQwOTYt # UjEuY2VyMCAGCCsGAQUFBzABhhRodHRwOi8vb2NzcHMuc3NsLmNvbTBRBgNVHSAE # SjBIMAgGBmeBDAEEATA8BgwrBgEEAYKpMAEDAwEwLDAqBggrBgEFBQcCARYeaHR0 # cHM6Ly93d3cuc3NsLmNvbS9yZXBvc2l0b3J5MBMGA1UdJQQMMAoGCCsGAQUFBwMD # ME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9jcmxzLnNzbC5jb20vU1NMY29tLVN1 # YkNBLUNvZGVTaWduaW5nLVJTQS00MDk2LVIxLmNybDAdBgNVHQ4EFgQUSj8HrSK7 # f/j+Dz31jJFhOF7rJUMwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IC # AQBf4lcc6FUJ1W/opNz8yjS9qLUy9cQt0s35BhasB5QoTbDaW4jv9xnFGhQVg6n+ # jhL0i94Vsywd/MRBb8lYGpuBZnS/7LHuRZu7qUuud+IMDyRHIyBK6koN5bfyA5VY # c7bFbNpbe1s1hMWke8di4qgMLZKDfyG/RtA0swf5t4UgQLPP0h+koZ8X8V5+P0V0 # 1HsdXyXd+ojo38EoZyCKfQL2aAwMPwzZfCbmI5SRXNOc6K8oqXzQcendhlKSfVBo # Zgpi+1updqbD4jmJfYdK5AYPxJ3YH6td6ETtr8owL+bmX8lQjlXPOwVnC11rVlNB # VjqtaJRUClLtiNiYSTKVfjdmGVJ4+sNov0dWhHc0A9o5NX/05VVYTlImuJpnG5Og # o7w6kWRdsgE8gM58jWf7XfI6aQS0Np/z2B+ZBj0K93khEHBX7cvvORa92LCHiVeP # km+zEAMXgxIPs/e8cmcc/o3CORgzEwxlH9Z3UOWCuXSHD3P2RPNDAY+WPdjSHm9f # JFlGq+f9iKyedxYa/NNjNag/5EbZ+Z2NldtSMNeFdsejGJ/TJHF1PyJd4aXx9J1i # B/IZBOoJYyh9xpQ3ljZUKE/4otPi7INpuDFwgWiUHZZJVvrGTWwxH1Yhf8P+VpFf # aNqsBuvklUcUDs3RNE0f1qlgFfcnAepFF+RiBRqmsj29fjGCEdAwghHMAgEBMIGM # MHgxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3Rv # bjERMA8GA1UECgwIU1NMIENvcnAxNDAyBgNVBAMMK1NTTC5jb20gQ29kZSBTaWdu # aW5nIEludGVybWVkaWF0ZSBDQSBSU0EgUjECEGdokrDW8DrI5yxMUgF+hegwDQYJ # YIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYK # KwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG # 9w0BCQQxIgQgWUquKsP0WqoguEa9/UWovO3Qtlfcf7ToSDl4vL8OfG0wDQYJKoZI # hvcNAQEBBQAEggGArnyXh9XPx9W0K5negSTQnJlMjfaLz7bwJ3jgpd54f7AqSiZo # /MjkbdzV8JkeaWlcg7Ci6H4zAqSF/hiZlKfCcIUYD7jEx/+wn3l0+kB4QcUguZU9 # nsmauZnYi5AoR4OGkesRdqwgLBC1rxDfON7zNCxj4DY6RBIfWrJXcjbFi8R0r2Hi # yKQBk7XghTEW+FB57QpD/NDuab8BARlgRWV70Ag8Tx/3T+SPr1V60TMn6CmStoTi # WyLVZax37eZfCJQh+e1FyvhJcJVlt6tRqzF5w7HRxD9n8ZxFkrD0ldmNQ+T6NqWU # Iub4apsbJIqB/Q+B/Lmp2V/9E6lXlifsWiwk2lid7vZV+oALWLxwLTdoxeplgI2h # hhqGGqidl1zMvwMcJRChL5RgBU32ekvx4Bkh+BeBE8IE4bY4F9igcsz9kXzAkqov # fa1Bluedcvg1RYnJX1+yQNMDrlFmpXwkMzNoxMWIy3/DUOPIq3Cxo0cFV27bkkJA # 2QfUsIq/njTfrp3yoYIPFjCCDxIGCisGAQQBgjcDAwExgg8CMIIO/gYJKoZIhvcN # AQcCoIIO7zCCDusCAQMxDTALBglghkgBZQMEAgEwdwYLKoZIhvcNAQkQAQSgaARm # MGQCAQEGDCsGAQQBgqkwAQMGATAxMA0GCWCGSAFlAwQCAQUABCDqFZriXhy5NKZ7 # RO3PccLH5iPnmRW7RvNyXtFGtMjPAgIIDWdZ9NB0ZX4YDzIwMjUwMzAyMTY1OTQ4 # WjADAgEBoIIMADCCBPwwggLkoAMCAQICEFparOgaNW60YoaNV33gPccwDQYJKoZI # hvcNAQELBQAwczELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQH # DAdIb3VzdG9uMREwDwYDVQQKDAhTU0wgQ29ycDEvMC0GA1UEAwwmU1NMLmNvbSBU # aW1lc3RhbXBpbmcgSXNzdWluZyBSU0EgQ0EgUjEwHhcNMjQwMjE5MTYxODE5WhcN # MzQwMjE2MTYxODE4WjBuMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO # BgNVBAcMB0hvdXN0b24xETAPBgNVBAoMCFNTTCBDb3JwMSowKAYDVQQDDCFTU0wu # Y29tIFRpbWVzdGFtcGluZyBVbml0IDIwMjQgRTEwWTATBgcqhkjOPQIBBggqhkjO # PQMBBwNCAASnYXL1MOl6xIMUlgVC49zonduUbdkyb0piy2i8t3JlQEwA74cjK8g9 # mRC8GH1cAAVMIr8M2HdZpVgkV1LXBLB8o4IBWjCCAVYwHwYDVR0jBBgwFoAUDJ0Q # JY6apxuZh0PPCH7hvYGQ9M8wUQYIKwYBBQUHAQEERTBDMEEGCCsGAQUFBzAChjVo # dHRwOi8vY2VydC5zc2wuY29tL1NTTC5jb20tdGltZVN0YW1waW5nLUktUlNBLVIx # LmNlcjBRBgNVHSAESjBIMDwGDCsGAQQBgqkwAQMGATAsMCoGCCsGAQUFBwIBFh5o # dHRwczovL3d3dy5zc2wuY29tL3JlcG9zaXRvcnkwCAYGZ4EMAQQCMBYGA1UdJQEB # /wQMMAoGCCsGAQUFBwMIMEYGA1UdHwQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmxzLnNz # bC5jb20vU1NMLmNvbS10aW1lU3RhbXBpbmctSS1SU0EtUjEuY3JsMB0GA1UdDgQW # BBRQTySs77U+YxMjCZIm7Lo6luRdIjAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcN # AQELBQADggIBAJigjwMAkbyrxGRBf0Ih4r+rbCB57lTuwViC6nH2fZSciMogpqSz # rSeVZ2eIb5vhj9rT7jqWXZn02Fncs4YTrA1QyxJW36yjC4jl5/bsFCaWuXzGXt2Y # 6Ifp//A3Z0sNTMWTTBobmceM3sqnovdX9ToRFP+29r5yQnPcgRTI2PvrVSqLxY9E # yk9/0cviM3W29YBl080ENblRcu3Y8RsfzRtVT/2snuDocRxvRYmd0TPaMgIj2xII # 651QnPp1hiq9xU0AyovLzbsi5wlR5Ip4i/i8+x+HwYJNety5cYtdWJ7uQP6YaZtW # /jNoHp76qNftq/IlSx6xEYBRjFBxHSq2fzhUQ5oBawk2OsZ2j0wOf7q7AqjCt6t/ # +fbmWjrAWYWZGj/RLjltqdFPBpIKqdhjVIxaGgzVhaE/xHKBg4k4DfFZkBYJ9BWu # P93Tm+paWBDwXI7Fg3alGsboErWPWlvwMAmpeJUjeKLZY26JPLt9ZWceTVWuIyuj # erqb5IMmeqLJm5iFq/Qy4YPGyPiolw5w1k9OeO4ErmS2FKvk1ejvw4SWR+S1VyWn # ktY442WaoStxBCCVWZdMWFeB+EpL8uoQNq1MhSt/sIUjUudkyZLIbMVQjj7b6gPX # nD6mS8FgWiCAhuM1a/hgA+6o1sJWizHdmcpYDhyNzorf9KVRE6iR7rcmMIIG/DCC # BOSgAwIBAgIQbVIYcIfoI02FYADQgI+TVjANBgkqhkiG9w0BAQsFADB8MQswCQYD # VQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAWBgNV # BAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBSb290IENlcnRp # ZmljYXRpb24gQXV0aG9yaXR5IFJTQTAeFw0xOTExMTMxODUwMDVaFw0zNDExMTIx # ODUwMDVaMHMxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwH # SG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxLzAtBgNVBAMMJlNTTC5jb20gVGlt # ZXN0YW1waW5nIElzc3VpbmcgUlNBIENBIFIxMIICIjANBgkqhkiG9w0BAQEFAAOC # Ag8AMIICCgKCAgEArlEQE9L5PCCgIIXeyVAcZMnh/cXpNP8KfzFI6HJaxV6oYf3x # h/dRXPu35tDBwhOwPsJjoqgY/Tg6yQGBqt65t94wpx0rAgTVgEGMqGri6vCI6rEt # SZVy9vagzTDHcGfFDc0Eu71mTAyeNCUhjaYTBkyANqp9m6IRrYEXOKdd/eREsqVD # mhryd7dBTS9wbipm+mHLTHEFBdrKqKDM3fPYdBOro3bwQ6OmcDZ1qMY+2Jn1o0l4 # N9wORrmPcpuEGTOThFYKPHm8/wfoMocgizTYYeDG/+MbwkwjFZjWKwb4hoHT2WK8 # pvGW/OE0Apkrl9CZSy2ulitWjuqpcCEm2/W1RofOunpCm5Qv10T9tIALtQo73GHI # lIDU6xhYPH/ACYEDzgnNfwgnWiUmMISaUnYXijp0IBEoDZmGT4RTguiCmjAFF5OV # NbY03BQoBb7wK17SuGswFlDjtWN33ZXSAS+i45My1AmCTZBV6obAVXDzLgdJ1A1r # yyXz4prLYyfJReEuhAsVp5VouzhJVcE57dRrUanmPcnb7xi57VPhXnCuw26hw1Hd # +ulK3jJEgbc3rwHPWqqGT541TI7xaldaWDo85k4lR2bQHPNGwHxXuSy3yczyOg57 # TcqqG6cE3r0KR6jwzfaqjTvN695GsPAPY/h2YksNgF+XBnUD9JBtL4c34AcCAwEA # AaOCAYEwggF9MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAU3QQJB6L1 # en1SUxKSle44gCUNplkwgYMGCCsGAQUFBwEBBHcwdTBRBggrBgEFBQcwAoZFaHR0 # cDovL3d3dy5zc2wuY29tL3JlcG9zaXRvcnkvU1NMY29tUm9vdENlcnRpZmljYXRp # b25BdXRob3JpdHlSU0EuY3J0MCAGCCsGAQUFBzABhhRodHRwOi8vb2NzcHMuc3Ns # LmNvbTA/BgNVHSAEODA2MDQGBFUdIAAwLDAqBggrBgEFBQcCARYeaHR0cHM6Ly93 # d3cuc3NsLmNvbS9yZXBvc2l0b3J5MBMGA1UdJQQMMAoGCCsGAQUFBwMIMDsGA1Ud # HwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmxzLnNzbC5jb20vc3NsLmNvbS1yc2EtUm9v # dENBLmNybDAdBgNVHQ4EFgQUDJ0QJY6apxuZh0PPCH7hvYGQ9M8wDgYDVR0PAQH/ # BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQCSGXUNplpCzxkH2fL8lPrAm/AV6USW # Wi9xM91Q5RN7mZN3D8T7cm1Xy7qmnItFukgdtiUzLbQokDJyFTrF1pyLgGw/2hU3 # FJEywSN8crPsBGo812lyWFgAg0uOwUYw7WJQ1teICycX/Fug0KB94xwxhsvJBiRT # pQyhu/2Kyu1Bnx7QQBA1XupcmfhbQrK5O3Q/yIi//kN0OkhQEiS0NlyPPYoRboHW # C++wogzV6yNjBbKUBrMFxABqR7mkA0x1Kfy3Ud08qyLC5Z86C7JFBrMBfyhfPpKV # lIiiTQuKz1rTa8ZW12ERoHRHcfEjI1EwwpZXXK5J5RcW6h7FZq/cZE9kLRZhvnRK # tb+X7CCtLx2h61ozDJmifYvuKhiUg9LLWH0Or9D3XU+xKRsRnfOuwHWuhWch8G7k # EmnTG9CtD9Dgtq+68KgVHtAWjKk2ui1s1iLYAYxnDm13jMZm0KpRM9mLQHBK5Gb4 # dFgAQwxOFPBslf99hXWgLyYE33vTIi9p0gYqGHv4OZh1ElgGsvyKdUUJkAr5hfbD # X6pYScJI8v9VNYm1JEyFAV9x4MpskL6kE2Sy8rOqS9rQnVnIyPWLi8N9K4GZvPit # /Oy+8nFL6q5kN2SZbox5d69YYFe+rN1sDD4CpNWwBBTI/q0V4pkgvhL99IV2Xasj # HZf4peSrHdL4RjGCAlgwggJUAgEBMIGHMHMxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI # DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxLzAt # BgNVBAMMJlNTTC5jb20gVGltZXN0YW1waW5nIElzc3VpbmcgUlNBIENBIFIxAhBa # WqzoGjVutGKGjVd94D3HMAsGCWCGSAFlAwQCAaCCAWEwGgYJKoZIhvcNAQkDMQ0G # CyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAzMDIxNjU5NDhaMCgGCSqG # SIb3DQEJNDEbMBkwCwYJYIZIAWUDBAIBoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJ # BDEiBCAy4Dmda6fZ8JuKAG55D9d/7Ktjc8qyHYVuxbpyAmBf+DCByQYLKoZIhvcN # AQkQAi8xgbkwgbYwgbMwgbAEIJ1xf43CN2Wqzl5KsOH1ddeaF9Qc7tj9r+8D/T29 # iUfnMIGLMHekdTBzMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNV # BAcMB0hvdXN0b24xETAPBgNVBAoMCFNTTCBDb3JwMS8wLQYDVQQDDCZTU0wuY29t # IFRpbWVzdGFtcGluZyBJc3N1aW5nIFJTQSBDQSBSMQIQWlqs6Bo1brRiho1XfeA9 # xzAKBggqhkjOPQQDAgRHMEUCIQDnKMGBWoZHX65bIX4t5NjZ3MZyeMNANiU68yfI # 7gKpQAIgROo5Cil79pmclH7pLyTeX0++3T9+mBXHOffZkoi+yfw= # SIG # End signature block |