ozo-ad-lab-create-vms.ps1
#Requires -Modules @{ModuleName="OZO";ModuleVersion="1.5.0"},@{ModuleName="OZOLogger";ModuleVersion="1.1.0"} -RunAsAdministrator <#PSScriptInfo .VERSION 1.0.0 .GUID e2071482-27a4-415c-b3db-43d5351d24bb .AUTHOR Andy Lievertz <alievertz@onezeroone.dev> .COMPANYNAME One Zero One .COPYRIGHT This script is licensed under the GNU Public License ("GPL") version 2.0. .TAGS .LICENSEURI https://github.com/onezeroone-dev/OZO-AD-Lab-Create-VMs/blob/main/LICENSE .PROJECTURI https://github.com/onezeroone-dev/OZO-AD-Lab-Create-VMs .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES https://github.com/onezeroone-dev/OZO-AD-Lab-Create-VMs/blob/main/CHANGELOG.md .PRIVATEDATA #> <# .DESCRIPTION An interactive script that creates a the Hyper-V virtual machines required for the One Zero One AD Lab. .PARAMETER ClientISO The path to the customized Client ISO. Defaults to C:\ozo-ad-lab\ISO\AD-Lab-Client.iso. .PARAMETER DCISO The path to the customized DC ISO. Defaults to C:\ozo-ad-lab\ISO\AD-Lab-DC.iso. .PARAMETER LaofISO The path to the Windows 11 Lanuages & Optional Features ISO. Defaults to C:\ozo-ad-lab\ISO\Windows-11-Lanuages-and-Optional-Features.iso. .PARAMETER RouterISO The path to the customized Router ISO. Defaults to C:\ozo-ad-lab\ISO\AD-Lab-Router.iso. .PARAMETER ServerISO The path for the VHDX files. Defaults to $Env:ProgramData\Microsoft\Windows\Virtual Hard Disks. .EXAMPLE ozo-ad-lab-create-vms .EXAMPLE $isoPath = (Join-Path -Path $Env:USERPROFILE -ChildPath "Downloads") ozo-ad-lab-create-vms -ClientISO "$isoPath\AD-Lab-Client.iso" -DCISO "$isoPath\AD-Lab-DC.iso" .LINK https://github.com/onezeroone-dev/OZO-AD-Lab-Create-VMs/blob/main/README.md .NOTES Run this script in an Administrator PowerShell. #> Param( [Parameter(Mandatory=$false,HelpMessage="The path to the Client ISO")][String]$ClientISO = "C:\ozo-ad-lab\ISO\AD-Lab-Client.iso", [Parameter(Mandatory=$false,HelpMessage="The path to the DC ISO")][String]$DCISO = "C:\ozo-ad-lab\ISO\AD-Lab-DC.iso", [Parameter(Mandatory=$false,HelpMessage="The path to the Lanugages & Optional Features ISO")][String]$LaofISO = "C:\ozo-ad-lab\ISO\Windows-11-Lanuages-and-Optional-Features.iso", [Parameter(Mandatory=$false,HelpMessage="The path to the Router ISO")][String]$RouterISO = "C:\ozo-ad-lab\ISO\AD-Lab-Router.iso", [Parameter(Mandatory=$false,HelpMessage="The path to the Server ISO")][String]$ServerISO = "C:\ozo-ad-lab\ISO\AD-Lab-Server.iso", [Parameter(Mandatory=$false,HelpMessage="The path for VHDX files")][String]$VHDXPath = (Join-Path -Path $Env:ProgramData -ChildPath "Microsoft\Windows\Virtual Hard Disks") ) # CLASSES Class ADLCVM { # PROPERTIES: Arrays, Strings [Array] $hyperVSwitches = @("AD Lab External","AD Lab Private") [String] $clientISO = $null [String] $dcISO = $null [String] $laofISO = $null [String] $routerISO = $null [String] $serverISO = $null [String] $vhdxPath = $null # PROPERTIES: PSCustomObjects [PSCustomObject] $ozoLogger = @{} # PROPERTIES: Lists [System.Collections.Generic.List[PSCustomObject]] $ozoVMs = @() # METHODS # Constructor method ADLCVM($ClientISO,$DCISO,$LaofISO,$RouterISO,$ServerISO,$VHDXPath) { # Set properties $this.clientISO = $clientISO $this.dcISO = $DCISO $this.laofISO = $LaofISO $this.routerISO = $RouterISO $this.serverISO = $ServerISO $this.vhdxPath = $VHDXPath $this.ozoLogger = (New-OZOLogger) # Announce ourselves to the world. $this.ozoLogger.Write("Process starting.","Information") $this.ozoLogger # Call validate configuration and validate environment to determine if we can proceed If (($this.ValidateConfiguration() -And $this.ValidateEnvironment()) -eq $true) { # Report $this.ozoLogger.Write("Configuration and environment validated; evaluating VMs.","Information") # Create the virtual machine objects $this.ozoVMs.Add(([ADLCVMVirtualMachine]::new("AD Lab 01 router.contoso.com","Linux",1,1073741824,68719476736,$this.vhdxPath,$this.routerISO,"AD Lab External","AD Lab Private"))) # Creat an ADLCVMVirtualMachine object for the DC $this.ozoVMs.Add(([ADLCVMVirtualMachine]::new("AD Lab 02 dc.contoso.com","Windows",1,2147483648,137438953472,$this.vhdxPath,$this.dcISO,"AD Lab Private",$null))) # Creat an ADLCVMVirtualMachine object for the Server $this.ozoVMs.Add(([ADLCVMVirtualMachine]::new("AD Lab 03 server.contoso.com","Windows",1,2147483648,137438953472,$this.vhdxPath,$this.serverISO,"AD Lab Private",$null))) # Creat an ADLCVMVirtualMachine object for the Client $this.ozoVMs.Add(([ADLCVMVirtualMachine]::new("AD Lab 04 client.contoso.com","Windows",2,4294967296,137438953472,$this.vhdxPath,$this.clientISO,"AD Lab Private",$null))) # Configuration and environment validate; iterate through the VM objects ForEach ($VM in $this.ozoVMs) { If ($VM.Create -eq $true) { # VM validated; call the CreateVM method to create the VM and set Created on the VM object If ($VM.CreateVM() -eq $true) { # VM was created $this.ozoLogger.Write(("Creating the " + $VM.vmName + " VM."),"Information") } Else { # VM was not created $this.ozoLogger.Write(("Error creating the " + $VM.vmName + " virtual machine for the following reasons:`r`n" + ($VM.Messages -Join("`r`n"))),"Warning") } } Else { # VM did not validate; report $this.ozoLogger.Write(("Skipping the " + $VM.vmName + " virtual machine for the following reasons:`r`n" + ($VM.Messages -Join("`r`n"))),"Warning") } } } Else { $this.ozoLogger.Write("Configuration and/or environment did not validate.","Error") } # Bid the world adieu. $this.ozoLogger.Write("Process complete.","Information") } # Validate configuration method Hidden [Boolean] ValidateConfiguration() { # Control variable [Boolean] $Return = $true ForEach ($Path in $this.clientISO,$this.dcISO,$this.laofISO,$this.serverISO) { # Determine if the path is invalid If ((Test-Path -Path $Path) -eq $false) { # Path is invalid $this.ozoLogger.Write(("Cannot find " + $Path),"Error") $Return = $false } } # Return return $Return } # Validate environment methods Hidden [Boolean] ValidateEnvironment() { # Control variable [Boolean] $Return = $true # Test if session is user-interactive 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 current user is an Administrator or a member of Hyper-V Administrators If ((Test-OZOLocalAdministrator) -eq $false -And (Test-OZOHyperVAdministrator) -eq $false) { # User is neither an Administrator or a Hyper-V Administrator $this.ozoLogger.Write("User is not an Administrator or a member of Hyper-V Administrators. Run this script in an Administrator PowerShell session or add your user to the local Hyper-V Administrators group.") $Return = $false } # Iterate through the Hyper-V virtual switches ForEach ($virtualSwitch in $this.hyperVSwitches) { # Determine if the switch is not present If ((Get-VMSwitch).Name -NotContains $virtualSwitch) { # Switch is not present $this.ozoLogger.Write(("Hyper-V virtual switch " + $virtualSwitch + " not found."),"Error") $Return = $false } } # Determine if the VHDXPath is not writable If ((Test-OZOPath -Writable -Path $this.vhdxPath) -eq $false) { # Path is not writable $this.ozoLogger.Write(("The VHDX path is not writable."),"Error") $Return = $false } # Determine if the Hyper-V Feature is not installed If ([Boolean](Get-WindowsOptionalFeature -Online -FeatureName "Microsoft-Hyper-V") -eq $false) { # Feature is not present $this.ozoLogger.Write(("The Hyper-V Feature is not installed. Please see https://onezeroone.dev/active-directory-lab-prerequisites."),"Error") $Return = $false } # Return return $Return } } Class ADLCVMVirtualMachine { # PROPERTIES: Arrays, Booleans, Ints, Strings [Array] $osList = @("Linux","Windows") [Boolean] $Create = $true [Int16] $vmVCPUs = $null [Int64] $vmDisk = $null [Int64] $vmMemory = $null [String] $vhdxPath = $null [String] $vmISO = $null [String] $vmName = $null [String] $vmOS = $null [String] $vmSBT = $null [String] $vmSwitch = $null [String] $vmSwit2h = $null # Properties: Lists [System.Collections.Generic.List[String]] $Messages = @() # METHODS # Constructor method ADLCVMVirtualMachine($Name,$OS,$vCPUs,$Memory,$Disk,$VHDXPath,$ISO,$Network,$Netwo2k) { # Set properties $this.vmName = $Name $this.vmOS = $OS $this.vmVCPUs = $vCPUs $this.vmMemory = $Memory $this.vmDisk = $Disk $this.vhdxPath = (Join-Path -Path $VHDXPath -ChildPath ($this.vmName + ".vhdx")) $this.vmISO = $ISO $this.vmSwitch = $Network $this.vmSwit2h = $Netwo2k # Switch on OS to set vmSBT Switch($this.vmOS) { "Linux" { $this.vmSBT = "MicrosoftUEFICertificateAuthority" } default { $this.vmSBT = "MicrosoftWindows" } } # Call ValidateVM to set Create $this.Create = $this.ValidateVM() } # Validate VM method Hidden [Boolean] ValidateVM() { # Control variable [Boolean] $Return = $true # Determine if the VM already exists If ((Get-VM).Name -Contains $this.vmName) { # VM exists; skipping $this.Messages.Add("VM already exists.") $Return = $false } If ((Test-Path -Path $this.vhdxPath) -eq $true) { # VM disk exists; skipping $this.Messages.Add("VM disk already exists.") $Return = $false } # Determine if OS is valid If ($this.osList -NotContains $this.vmOS) { # OS is not handled $this.Messages.Add(("Operating system " + $this.vmOS + " is invalid.")) } # Return return $Return } # Create VM method [Boolean] CreateVM() { # Control variables [Boolean] $Return = $true # Create the VM Try { New-VM -Name $this.vmName -Generation 2 -MemoryStartupBytes $this.vmMemory -SwitchName $this.vmSwitch -NewVHDPath $this.vhdxPath -NewVHDSizeBytes $this.vmDisk -ErrorAction Stop # Success; configure processor count, automagic start action, and automagic stop action; and disable checkpoints Set-VM -Name $this.vmName -ProcessorCount $this.vmVCPUs -AutomaticStartAction "Nothing" -AutomaticStopAction "Shutdown" -CheckpointType "Disabled" # Configure Memory and Memory Buffer Set-VMMemory -VMName $this.vmName -DynamicMemoryEnabled $true -MinimumBytes 536870912 -MaximumBytes $this.vmMemory -Buffer 5 -ErrorAction Stop # Configure firmware Set-VMFirmware -VMName $this.vmName -EnableSecureBoot "On" -SecureBootTemplate $this.vmSBT -ErrorAction Stop # Enable TPM and Key Protector Set-VMKeyProtector -VMName $this.vmName -NewLocalKeyProtector Enable-VMTPM -VMName $this.vmName -ErrorAction Stop # Configure the DVD drive Add-VMDvdDrive -VMName $this.vmName -Path $this.vmISO # Set DVD drive as first boot option Set-VMFirmware -VMName $this.vmName -FirstBootDevice (Get-VMDvdDrive -VMName $this.vmName) # Configure integrations Enable-VMIntegrationService -VMName $this.vmName -Name "Guest Service Interface","Heartbeat","Key-Value Pair Exchange","Shutdown","Time Synchronization","VSS" -ErrorAction Stop # Determine if this VM should ahve a second vSwitch If ([String]::IsNullOrEmpty($this.vmSwit2h) -eq $false) { # Add second vSwitch Add-VMNetworkAdapter -VMName $this.vmName -SwitchName $this.vmSwit2h -ErrorAction Stop } } Catch { # Failure $this.Messages.Add(("Error creating or configuring " + $this.vmName + " VM. Error message is " + $_ + "`r`nDeleting.")) $Return = $false # Determine if the VM was created in any capacity or configuration If ([Boolean](Get-VM -VMName $this.vmName) -eq $true) { # VM was created; try to remove it Try { Remove-VM -VMName $this.vmName -Force -ErrorAction Stop # Success; determine if a VHDX was created If ((Test-Path -Path $this.vhdxPath) -eq $true) { # VHDX was created; remove it Remove-Item -Path $this.vhdxPath -Force -ErrorAction Stop } } Catch { # Failure $this.Messages.Add(("Unable to delete the " + $this.vmName + " VM. AD Lab configuration may be in an inconsistent state.")) } } } # Return return $Return } } # FUNCTIONS Function Test-OZOHyperVAdministrator { # Determine if current user is a member of Administrators or Hyper-V Administrators If ((([ADSI]"WinNT://./Hyper-V Administrators,group").psbase.Invoke("Members") | ForEach-Object {$_.GetType().InvokeMember("Name",'GetProperty', $null, $_, $null)}) -Contains [System.Environment]::UserName) { # User is a member of the Hyper-V Administrators group return $true } Else { # User not a member of the Hyper-V Administrators group return $false } } # MAIN [ADLCVM]::new($ClientISO,$DCISO,$LaofISO,$RouterISO,$ServerISO,$VHDXPath) | Out-Null # SIG # Begin signature block # MIIfcgYJKoZIhvcNAQcCoIIfYzCCH18CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDJ+Ln7c3heY8/S # xEc4lBw43Yfu+3QHJO42J2TePVrtC6CCDPgwggZyMIIEWqADAgECAghkM1HTxzif # 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 # 9w0BCQQxIgQgfgl5UDls+n69WhG6xwN7dh1WPghsW3LS8VeCDZocQKwwDQYJKoZI # hvcNAQEBBQAEggGAWciUP5IKAQUrcOx807VGnFmwhTk2yMREZ4vdn2xzPw+r2lO8 # XdDYvDQX0gpgDJmJRtf58dpINA1aZhNUsctS3LaPz4FB+HPkuJ9UkUzNz1BfymEH # 2arR3jb0KfnU0vXHcByZV+9UK9D0ZZDSX13zVT/MqfpyGW7fQHhFWOsZzfcnEUsY # SEUqL5bFAIfhbzRIqwoL7JIhtogQdjEnsvOFJaPZCOqHVuTNBSC3tfHU4n2b25dQ # +6i49ELlaq8PLDTjdbMIqXoxvtkYZE7dsTzx9QRB8ZKW+As3YOX7KNis5b7AgbgL # wMlfqpd3gUl5IcbpMdfBOY3XUr3JBifnMWj0V9qAaq8kKm/lQOzdtEylXh+Md1t+ # mC47V3ncO64BwEstvgXUqCi/Ht/q/xW5PNGwafsnbv+ZazbV27oxs85CKMMkoz4V # 8mDNpoYf9cTHEUsXr4PygQD8GJTgRndbnZ73oXW0nE5YjfEAScnURx53VIMoh+VR # 0t8ewQ/n8ALYlmLxoYIPFjCCDxIGCisGAQQBgjcDAwExgg8CMIIO/gYJKoZIhvcN # AQcCoIIO7zCCDusCAQMxDTALBglghkgBZQMEAgEwdwYLKoZIhvcNAQkQAQSgaARm # MGQCAQEGDCsGAQQBgqkwAQMGATAxMA0GCWCGSAFlAwQCAQUABCBPBleJWJ0WbR3X # ewjRdnzPbroSz99+zqpDDU6r2BU9LQIII0LTyBuF2MgYDzIwMjUwMjE2MjAxMDQ5 # 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 # CyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMTYyMDEwNDlaMCgGCSqG # SIb3DQEJNDEbMBkwCwYJYIZIAWUDBAIBoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJ # BDEiBCDE0zhFRjUv3mYrNzNlCw7l/HvKFE57wlgXeQu3ZRiJsjCByQYLKoZIhvcN # AQkQAi8xgbkwgbYwgbMwgbAEIJ1xf43CN2Wqzl5KsOH1ddeaF9Qc7tj9r+8D/T29 # iUfnMIGLMHekdTBzMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNV # BAcMB0hvdXN0b24xETAPBgNVBAoMCFNTTCBDb3JwMS8wLQYDVQQDDCZTU0wuY29t # IFRpbWVzdGFtcGluZyBJc3N1aW5nIFJTQSBDQSBSMQIQWlqs6Bo1brRiho1XfeA9 # xzAKBggqhkjOPQQDAgRHMEUCIFKQjJb8aspRAiB6PlfpBzW5wXyRP0wDTns4tR1n # Xn1eAiEA//5C2IRovXpKU08AAxKz+CNI2bUNwuQ4LAq67YF/b0U= # SIG # End signature block |