# This powershell script is part of WVDAdmin and Project Hydra - see https://blog.itprocloud.de/Windows-Virtual-Desktop-Admin/ for more information # Current Version of this script: 4.7 param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ValidateSet('Generalize','JoinDomain','DataPartition','RDAgentBootloader','RestartBootloader','StartBootloader','CleanFirstStart', 'RenameComputer')] [string] $Mode, [string] $StrongGeneralize='0', [string] $ComputerNewname='', #Only for SecureBoot process (workaround, normaly not used) [string] $LocalAdminName='localAdmin', #Only for SecureBoot process (workaround, normaly not used) [string] $LocalAdminPassword='', [string] $DomainJoinUserName='', [string] $DomainJoinUserPassword='', [string] $LocalAdminName64='bG9jYWxBZG1pbg==', #Base64-coding is used if not empty - providing the older parameters to be compatible [string] $LocalAdminPassword64='', [string] $DomainJoinUserName64='', [string] $DomainJoinUserPassword64='', [string] $DomainJoinOU='', [string] $AadOnly='0', [string] $JoinMem='0', [string] $MovePagefileToC='0', [string] $DomainFqdn='', [string] $WvdRegistrationKey='', [string] $LogDir="$env:windir\system32\logfiles", [string] $HydraAgentUri='', #Only used by Hydra [string] $HydraAgentSecret='', #Only used by Hydra [string] $DownloadNewestAgent='0' #Download the newes agent, event if a local agent exist ) function LogWriter($message) { $message="$(Get-Date ([datetime]::UtcNow) -Format "o") $message" write-host($message) if ([System.IO.Directory]::Exists($LogDir)) {try {write-output($message) | Out-File $LogFile -Append} catch {}} } function ShowDrives() { $drives = Get-WmiObject -Class win32_volume -Filter "DriveType = 3" LogWriter("Drives:") foreach ($drive in $drives) { LogWriter("Name: '$($drive.Name)', Letter: '$($drive.DriveLetter)', Label: '$($drive.Label)'") } } function ShowPageFiles() { $pageFiles = Get-WmiObject -Class Win32_PageFileSetting LogWriter("Pagefiles:") foreach ($pageFile in $pageFiles) { LogWriter("Name: '$($pageFile.Name)', Maximum size: '$($pageFile.MaximumSize)'") } } function RedirectPageFileToC() { $CurrentPageFile = Get-WmiObject -Query 'select * from Win32_PageFileSetting' LogWriter("Pagefile name: '$($CurrentPageFile.Name)', max size: $($CurrentPageFile.MaximumSize)") $CurrentPageFile.delete() LogWriter("Pagefile deleted") $CurrentPageFile = Get-WmiObject -Query 'select * from Win32_PageFileSetting' if ($null -eq $CurrentPageFile) { LogWriter("Pagefile deletion successful") } else { LogWriter("Pagefile deletion failed") } Set-WMIInstance -Class Win32_PageFileSetting -Arguments @{name='c:\pagefile.sys';InitialSize = 0; MaximumSize = 0} $CurrentPageFile = Get-WmiObject -Query 'select * from Win32_PageFileSetting' if ($null -eq $CurrentPageFile) { LogWriter("Pagefile not found") } else { LogWriter("New pagefile name: '$($CurrentPageFile.Name)', max size: $($CurrentPageFile.MaximumSize)") } } function UnzipFile ($zipfile, $outdir) { # Based on https://gist.github.com/nachivpn/3e53dd36120877d70aee Add-Type -AssemblyName System.IO.Compression.FileSystem $files = [System.IO.Compression.ZipFile]::OpenRead($zipfile) foreach ($entry in $files.Entries) { $targetPath = [System.IO.Path]::Combine($outdir, $entry.FullName) $directory = [System.IO.Path]::GetDirectoryName($targetPath) if(!(Test-Path $directory )){ New-Item -ItemType Directory -Path $directory | Out-Null } if(!$targetPath.EndsWith("/")){ [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $targetPath, $true); } } } function DownloadFile ( $url, $outFile) { $i=3 $ok=$false; do { try { LogWriter("Try to download file") Invoke-WebRequest -Uri $url -OutFile $outFile -UseBasicParsing $ok=$true } catch { $i--; if ($i -le 0) { throw } LogWriter("Re-trying download after 10 seconds") Start-Sleep -Seconds 10 } } while (!$ok) } # Define static variables $LocalConfig="C:\ITPC-WVD-PostCustomizing" $unattend="PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0ndXRmLTgnPz48dW5hdHRlbmQgeG1sbnM9InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206dW5hdHRlbmQiPjxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj48Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPjxPT0JFPjxTa2lwTWFjaGluZU9PQkU+dHJ1ZTwvU2tpcE1hY2hpbmVPT0JFPjxTa2lwVXNlck9PQkU+dHJ1ZTwvU2tpcFVzZXJPT0JFPjwvT09CRT48L2NvbXBvbmVudD48L3NldHRpbmdzPjwvdW5hdHRlbmQ+" # Define logfile $LogFile=$LogDir+"\AVD.Customizing.log" # Main LogWriter("Starting ITPC-WVD-Image-Processing in mode ${Mode}") # Generating variables from Base64-coding if ($LocalAdminName64) {$LocalAdminName=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($LocalAdminName64))} if ($LocalAdminPassword64) {$LocalAdminPassword=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($LocalAdminPassword64))} if ($DomainJoinUserName64) {$DomainJoinUserName=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($DomainJoinUserName64))} if ($DomainJoinUserPassword64) {$DomainJoinUserPassword=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($DomainJoinUserPassword64))} # check for the existend of the helper scripts if ((Test-Path ($LocalConfig+"\ITPC-WVD-Image-Processing.ps1")) -eq $false) { # Create local directory for script(s) and copy files (including the RD agent and boot loader - rename it to the specified name) LogWriter("Copy files to local session host or downloading files from Microsoft") new-item $LocalConfig -ItemType Directory -ErrorAction Ignore try {(Get-Item $LocalConfig -ErrorAction Ignore).attributes="Hidden"} catch {} if ((Test-Path ("${PSScriptRoot}\ITPC-WVD-Image-Processing.ps1")) -eq $false) { LogWriter("Creating ITPC-WVD-Image-Processing.ps1") Copy-Item "$($MyInvocation.InvocationName)" -Destination ($LocalConfig+"\ITPC-WVD-Image-Processing.ps1") } else {Copy-Item "${PSScriptRoot}\ITPC-WVD-Image-Processing.ps1" -Destination ($LocalConfig+"\")} } if ($ComputerNewname -eq "" -or $DownloadNewestAgent -eq "1") { if ((Test-Path ($LocalConfig+"\Microsoft.RDInfra.RDAgent.msi")) -eq $false -or $DownloadNewestAgent -eq "1") { if ((Test-Path ($ScriptRoot+"\Microsoft.RDInfra.RDAgent.msi")) -eq $false -or $DownloadNewestAgent -eq "1") { LogWriter("Downloading RDAgent") DownloadFile "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWrmXv" ($LocalConfig+"\Microsoft.RDInfra.RDAgent.msi") #Invoke-WebRequest -Uri "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWrmXv" -OutFile ($LocalConfig+"\Microsoft.RDInfra.RDAgent.msi") } else {Copy-Item "${PSScriptRoot}\Microsoft.RDInfra.RDAgent.msi" -Destination ($LocalConfig+"\")} } if ((Test-Path ($LocalConfig+"\Microsoft.RDInfra.RDAgentBootLoader.msi")) -eq $false -or $DownloadNewestAgent -eq "1") { if ((Test-Path ($ScriptRoot+"\Microsoft.RDInfra.RDAgentBootLoader.msi ")) -eq $false -or $DownloadNewestAgent -eq "1") { LogWriter("Downloading RDBootloader") DownloadFile "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWrxrH" ($LocalConfig+"\Microsoft.RDInfra.RDAgentBootLoader.msi") #Invoke-WebRequest -Uri "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWrxrH" -OutFile ($LocalConfig+"\Microsoft.RDInfra.RDAgentBootLoader.msi") } else {Copy-Item "${PSScriptRoot}\Microsoft.RDInfra.RDAgentBootLoader.msi" -Destination ($LocalConfig+"\")} } } # updating local script (from maybe an older version from the last image process) Copy-Item "$($MyInvocation.InvocationName)" -Destination ($LocalConfig+"\ITPC-WVD-Image-Processing.ps1") -Force -ErrorAction SilentlyContinue # check, if secure boot is enabled (used by the snapshot workaround) $isSecureBoot=$false try { $isSecureBoot=Confirm-SecureBootUEFI } catch {} # Start script by mode if ($mode -eq "Generalize") { LogWriter("Removing existing Remote Desktop Agent Boot Loader") Uninstall-Package -Name "Remote Desktop Agent Boot Loader" -AllVersions -Force -ErrorAction SilentlyContinue LogWriter("Removing existing Remote Desktop Services Infrastructure Agent") Uninstall-Package -Name "Remote Desktop Services Infrastructure Agent" -AllVersions -Force -ErrorAction SilentlyContinue Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\RDMonitoringAgent" -Force -ErrorAction Ignore LogWriter("Disabling ITPC-LogAnalyticAgent and MySmartScale if exist") Disable-ScheduledTask -TaskName "ITPC-LogAnalyticAgent for RDS and Citrix" -ErrorAction Ignore Disable-ScheduledTask -TaskName "ITPC-MySmartScaleAgent" -ErrorAction Ignore LogWriter("Cleaning up reliability messages") $key="HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reliability" Remove-ItemProperty -Path $key -Name "DirtyShutdown" -ErrorAction Ignore Remove-ItemProperty -Path $key -Name "DirtyShutdownTime" -ErrorAction Ignore Remove-ItemProperty -Path $key -Name "LastAliveStamp" -ErrorAction Ignore Remove-ItemProperty -Path $key -Name "TimeStampInterval" -ErrorAction Ignore # Triggering dotnet to execute queued items $dotnetRoot="$env:windir\Microsoft.NET\Framework" Get-ChildItem -Path $dotnetRoot -Directory | foreach { if (Test-Path "$($_.FullName)\ngen.exe") { LogWriter("Triggering dotnet to execute queued items in: $($_.FullName)") Start-Process -FilePath "$($_.FullName)\ngen.exe" -Wait -ArgumentList "ExecuteQueuedItems" -ErrorAction SilentlyContinue } } # Read property from registry (force imaging, like dism) $force=$StrongGeneralize -eq "1" if (Test-Path -Path "HKLM:\SOFTWARE\ITProCloud\WVD.Force") { $force=$true } # DISM cleanup if ($force -and (Test-Path "$env:windir\system32\Dism.exe")) { LogWriter("DISM cleanup") Start-Process -FilePath "$env:windir\system32\Dism.exe" -Wait -ArgumentList "/online /cleanup-image /startcomponentcleanup /resetbase" -ErrorAction SilentlyContinue } LogWriter("Modifying sysprep to avoid issues with AppXPackages - Start") $sysPrepActionPath="$env:windir\System32\Sysprep\ActionFiles" $sysPrepActionFile="Generalize.xml" $sysPrepActionPathItem = Get-Item $sysPrepActionPath.Replace("C:\","\\localhost\\c$\") -ErrorAction Ignore $acl = $sysPrepActionPathItem.GetAccessControl() $acl.SetOwner((New-Object System.Security.Principal.NTAccount("SYSTEM"))) $sysPrepActionPathItem.SetAccessControl($acl) $aclSystemFull = New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM","FullControl","Allow") $acl.AddAccessRule($aclSystemFull) $sysPrepActionPathItem.SetAccessControl($acl) [xml]$xml = Get-Content -Path "$sysPrepActionPath\$sysPrepActionFile" $xmlNode=$xml.sysprepInformation.imaging | where {$_.sysprepModule.moduleName -match "AppxSysprep.dll"} if ($xmlNode -ne $null) { $xmlNode.ParentNode.RemoveChild($xmlNode) $xml.sysprepInformation.imaging.Count $xml.Save("$sysPrepActionPath\$sysPrepActionFile.new") Remove-Item "$sysPrepActionPath\$sysPrepActionFile.old" -Force -ErrorAction Ignore Move-Item "$sysPrepActionPath\$sysPrepActionFile" "$sysPrepActionPath\$sysPrepActionFile.old" Move-Item "$sysPrepActionPath\$sysPrepActionFile.new" "$sysPrepActionPath\$sysPrepActionFile" LogWriter("Modifying sysprep to avoid issues with AppXPackages - Done") } # Preparation for the snapshot workaround if ($isSecureBoot -and $LocalAdminName -ne "" -and $LocalAdminPassword -ne "") { LogWriter("Creating administrator $LocalAdminName") New-LocalUser "$LocalAdminName" -Password (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force) -FullName "$LocalAdminName" -Description "Local Administrator" -ErrorAction SilentlyContinue Add-LocalGroupMember -Group "Administrators" -Member "$LocalAdminName" -ErrorAction SilentlyContinue } LogWriter("Removing an older Sysprep state") Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\Sysprep" -Name "SysprepCorrupt" -ErrorAction Ignore New-ItemProperty -Path "HKLM:\SYSTEM\Setup\Status\SysprepStatus" -Name "State" -Value 2 -force New-ItemProperty -Path "HKLM:\SYSTEM\Setup\Status\SysprepStatus" -Name "GeneralizationState" -Value 7 -force New-Item -Path "HKLM:\Software\Microsoft\DesiredStateConfiguration" -ErrorAction Ignore New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\DesiredStateConfiguration" -Name "AgentId" -Value "" -force -ErrorAction Ignore LogWriter("Saving time zone info for re-deploy") $timeZone=(Get-TimeZone).Id LogWriter("Current time zone is: "+$timeZone) New-Item -Path "HKLM:\SOFTWARE" -Name "ITProCloud" -ErrorAction Ignore New-Item -Path "HKLM:\SOFTWARE\ITProCloud" -Name "WVD.Runtime" -ErrorAction Ignore New-ItemProperty -Path "HKLM:\SOFTWARE\ITProCloud\WVD.Runtime" -Name "TimeZone.Origin" -Value $timeZone -force LogWriter("Removing existing Azure Monitoring Certificates") Get-ChildItem "Cert:\LocalMachine\Microsoft Monitoring Agent" -ErrorAction Ignore | Remove-Item # Check, if the optimization script exist (Hydra: use a script inside Hydra) if ([System.IO.File]::Exists("C:\ProgramData\Optimize\Win10_VirtualDesktop_Optimize.ps1")) { LogWriter("Running VDI Optimization script") Start-Process -wait -FilePath PowerShell.exe -WorkingDirectory "C:\ProgramData\Optimize" -ArgumentList '-ExecutionPolicy Bypass -File "C:\ProgramData\Optimize\Win10_VirtualDesktop_Optimize.ps1 -AcceptEULA -Optimizations WindowsMediaPlayer,AppxPackages,ScheduledTasks,DefaultUserSettings,Autologgers,Services,NetworkOptimizations"' -RedirectStandardOutput "$($LogDir)\VirtualDesktop_Optimize.Stage1.Out.txt" -RedirectStandardError "$($LogDir)\VirtualDesktop_Optimize.Stage1.Warning.txt" } # prepare cleanup task for new deployed VMs - solve an issue with the runcommand api giving older log data LogWriter("Preparing CleanFirstStart task") $action = New-ScheduledTaskAction -Execute "$env:windir\System32\WindowsPowerShell\v1.0\Powershell.exe" -Argument "-executionPolicy Unrestricted -File `"$LocalConfig\ITPC-WVD-Image-Processing.ps1`" -Mode `"CleanFirstStart`"" $trigger = New-ScheduledTaskTrigger -AtStartup $principal = New-ScheduledTaskPrincipal 'NT Authority\SYSTEM' -RunLevel Highest $settingsSet = New-ScheduledTaskSettingsSet $task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $settingsSet Register-ScheduledTask -TaskName 'ITPC-AVD-CleanFirstStart-Helper' -InputObject $task -ErrorAction Ignore Enable-ScheduledTask -TaskName 'ITPC-AVD-CleanFirstStart-Helper' LogWriter("Added new startup task to run the CleanFirstStart") # check if D:-drive not the temporary storage and having three drives $modifyDrives=$false $disks=Get-WmiObject -Class win32_volume | Where-Object { $_.DriveLetter -ne $null -and $_.DriveType -eq 3 } foreach ($disk in $disks) {if ($disk.Name -ne 'D:\' -and $disk.Label -eq 'Temporary Storage') {$modifyDrives=$true}} if ($disks.Count -eq 3 -and $modifyDrives) { LogWriter("VM with 3 drives so prepare change of drive letters of temp and data after deployment") New-ItemProperty -Path "HKLM:\SOFTWARE\ITProCloud\WVD.Runtime" -Name "ChangeDrives" -Value 1 -force # check if default value 'automatic manage pagefile size for all devices' is activated if ($null -eq (Get-WmiObject Win32_Pagefile) ) { # disable 'automatic manage pagefile size for all devices' $sys = Get-WmiObject Win32_Computersystem -EnableAllPrivileges $sys.AutomaticManagedPagefile = $false $sys.put() LogWriter("Automatic manage pagefile size for all devices deactivated") } else { LogWriter("Automatic manage pagefile size for all devices not activated") } # redirect pagefile to C: to rename data partition after deployment RedirectPageFileToC } LogWriter("Starting sysprep to generalize session host") if ([System.Environment]::OSVersion.Version.Major -le 6) { #Windows 7 LogWriter("Enabling RDP8 on Windows 7") New-Item -Path "HKLM:\SOFTWARE" -Name "Policies" -ErrorAction Ignore New-Item -Path "HKLM:\SOFTWARE\Policies" -Name "Microsoft" -ErrorAction Ignore New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft" -Name "Windows NT" -ErrorAction Ignore New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT" -Name "Terminal Services" -ErrorAction Ignore New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" -Name "fServerEnableRDP8" -Value 1 -force Start-Process -FilePath "$env:windir\System32\Sysprep\sysprep" -ArgumentList "/generalize /oobe /shutdown" } else { if ($isSecureBoot) { LogWriter("Secure boot is enabled") write-output([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($unattend))) | Out-File "$LocalConfig\unattend.xml" -Encoding ASCII write-output([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($unattend))) | Out-File "$env:windir\panther\unattend.xml" -Encoding ASCII Start-Process -FilePath "$env:windir\System32\Sysprep\sysprep" -ArgumentList "/generalize /oobe /shutdown /mode:vm /unattend:$LocalConfig\unattend.xml" } else { Start-Process -FilePath "$env:windir\System32\Sysprep\sysprep" -ArgumentList "/generalize /oobe /shutdown /mode:vm" } } } elseif ($mode -eq "RenameComputer") { # Used for the snapshot workaround LogWriter("Renaming computer to: "+$readComputerNewname) Rename-Computer -NewName $ComputerNewname -Force -ErrorAction SilentlyContinue } elseif ($mode -eq "JoinDomain") { # Removing existing agent if exist LogWriter("Removing existing Remote Desktop Agent Boot Loader") Uninstall-Package -Name "Remote Desktop Agent Boot Loader" -AllVersions -Force -ErrorAction SilentlyContinue LogWriter("Removing existing Remote Desktop Services Infrastructure Agent") Uninstall-Package -Name "Remote Desktop Services Infrastructure Agent" -AllVersions -Force -ErrorAction SilentlyContinue Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\RDMonitoringAgent" -Force -ErrorAction Ignore # Storing AadOnly to registry LogWriter("Storing AadOnly to registry: "+$AadOnly) New-Item -Path "HKLM:\SOFTWARE" -Name "ITProCloud" -ErrorAction Ignore New-Item -Path "HKLM:\SOFTWARE\ITProCloud" -Name "WVD.Runtime" -ErrorAction Ignore New-ItemProperty -Path "HKLM:\SOFTWARE\ITProCloud\WVD.Runtime" -Name "AadOnly" -Value $AadOnly -force # Checking for a saved time zone information if (Test-Path -Path "HKLM:\SOFTWARE\ITProCloud\WVD.Runtime") { $timeZone=(Get-ItemProperty -Path "HKLM:\SOFTWARE\ITProCloud\WVD.Runtime" -ErrorAction Ignore)."TimeZone.Origin" if ($timeZone -ne "" -and $timeZone -ne $null) { LogWriter("Setting time zone to: "+$timeZone) Set-TimeZone -Id $timeZone } } # AD / AAD handling LogWriter("Cleaning up previous AADLoginExtension / AAD join") Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows Azure\CurrentVersion\AADLoginForWindowsExtension" -Force -ErrorAction Ignore if (Test-Path -Path "$($env:WinDir)\system32\Dsregcmd.exe") { Start-Process -wait -FilePath "$($env:WinDir)\system32\Dsregcmd.exe" -ArgumentList "/leave" -ErrorAction SilentlyContinue } if ($DomainJoinUserName -ne "" -and $AadOnly -ne "1") { LogWriter("Joining AD domain") $psc = New-Object System.Management.Automation.PSCredential($DomainJoinUserName, (ConvertTo-SecureString $DomainJoinUserPassword -AsPlainText -Force)) $retry=3 $ok=$false do{ try { if ($DomainJoinOU -eq "") { Add-Computer -DomainName $DomainFqdn -Credential $psc -Force -ErrorAction Stop $ok=$true LogWriter("Domain joined successfully") } else { Add-Computer -DomainName $DomainFqdn -OUPath $DomainJoinOU -Credential $psc -Force -ErrorAction Stop $ok=$true LogWriter("Domain joined successfully") } } catch { if ($retry -eq 0) {throw $_} $retry-- LogWriter("Retry domain join because of an error: $_") Start-Sleep -Seconds 10 } } while($ok -ne $true) } else { LogWriter("AAD only is selected. Skipping joining to a native AD, joining AAD") $aadPath=@(Get-ChildItem -Directory "C:\Packages\Plugins\Microsoft.Azure.ActiveDirectory.AADLoginForWindows")[@(Get-ChildItem -Directory "C:\Packages\Plugins\Microsoft.Azure.ActiveDirectory.AADLoginForWindows").count-1].fullname Start-Process -wait -FilePath "$aadPath\AADLoginForWindowsHandler.exe" -WorkingDirectory $aadPath -ArgumentList 'enable' -RedirectStandardOutput "$($LogDir)\Avd.AadJoin.Out.txt" -RedirectStandardError "$($LogDir)\Avd.AadJoin.Warning.txt" if ($JoinMem -eq "1") { LogWriter("Joining Microsoft Endpoint Manamgement is selected. Try to register to MEM") Start-Process -wait -FilePath "$($env:WinDir)\system32\Dsregcmd.exe" -ArgumentList "/AzureSecureVMJoin /debug /MdmId 0000000a-0000-0000-c000-000000000000" -RedirectStandardOutput "$($LogDir)\Avd.MemJoin.Out.txt" -RedirectStandardError "$($LogDir)\Avd.MemJoin.Warning.txt" } try { if ($AadOnly) { $timeOut=(Get-Date).AddSeconds(5*60) do { LogWriter("Waiting for the domain join") Start-Sleep -Seconds 3#AzureAdJoined : YES } while ((Get-Date) -le $timeOut -and (Select-String -InputObject (&dsregcmd /status) -pattern "AzureAdJoined : YES").length -eq 0) } } catch {} } # check for disk handling $modifyDrives=$false if (Test-Path -Path "HKLM:\SOFTWARE\ITProCloud\WVD.Runtime") { if ((Get-ItemProperty -Path "HKLM:\SOFTWARE\ITProCloud\WVD.Runtime").ChangeDrives -eq 1) { $disks=Get-WmiObject -Class win32_volume | Where-Object { $_.DriveLetter -ne $null -and $_.DriveType -eq 3 } foreach ($disk in $disks) {if ($disk.Name -eq 'D:\' -and $disk.Label -eq 'Temporary Storage') {$modifyDrives=$true}} if ($modifyDrives -and $disks.Count -eq 3) { # change drive letters of temp and data drive for VMs with 3 drives LogWriter("VM with 3 drives so delete old pagefile and install runonce key") # create scheduled task executed at startup for next phase $action = New-ScheduledTaskAction -Execute "$env:windir\System32\WindowsPowerShell\v1.0\Powershell.exe" -Argument "-executionPolicy Unrestricted -File `"$LocalConfig\ITPC-WVD-Image-Processing.ps1`" -Mode `"DataPartition`"" $trigger = New-ScheduledTaskTrigger -AtStartup $principal = New-ScheduledTaskPrincipal 'NT Authority\SYSTEM' -RunLevel Highest $settingsSet = New-ScheduledTaskSettingsSet $task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $settingsSet Register-ScheduledTask -TaskName 'ITPC-AVD-Disk-Mover-Helper' -InputObject $task -ErrorAction Ignore Enable-ScheduledTask -TaskName 'ITPC-AVD-Disk-Mover-Helper' LogWriter("Added new startup task for the disk handling") # change c:\pagefile.sys to e:\pagefile.sys ShowPageFiles $CurrentPageFile = Get-WmiObject -Query 'select * from Win32_PageFileSetting' if ($null -eq $CurrentPageFile) { LogWriter("No pagefile found") } else { if ($CurrentPageFile.Name.tolower().contains('d:')) { $CurrentPageFile.delete() LogWriter("Old pagefile deleted") Set-WMIInstance -Class Win32_PageFileSetting -Arguments @{name='c:\pagefile.sys';InitialSize = 0; MaximumSize = 0} LogWriter("Set pagefile to c:\pagefile.sys") ShowPageFiles } } ShowPageFiles } } } # check to move pagefile finally to C if ($MovePagefileToC -eq "1") { RedirectPageFileToC } # install Hydra Agent (Hydra only) if ($HydraAgentUri -ne "") { $uri=$HydraAgentUri $secret=$HydraAgentSecret $DownloadAdress="https://$($uri)/Download/HydraAgent" try { if ((Test-Path ("$env:ProgramFiles\ITProCloud.de")) -eq $false) { new-item "$env:ProgramFiles\ITProCloud.de" -ItemType Directory -ErrorAction Ignore } if ((Test-Path ("$env:ProgramFiles\ITProCloud.de\HydraAgent")) -eq $false) { new-item "$env:ProgramFiles\ITProCloud.de\HydraAgent" -ItemType Directory -ErrorAction Ignore } Remove-Item -Path "$env:ProgramFiles\ITProCloud.de\HydraAgent\HydraAgent.zip" -Force -ErrorAction Ignore LogWriter("Downloading HydraAgent.zip from $DownloadAdress") DownloadFile $DownloadAdress "$env:ProgramFiles\ITProCloud.de\HydraAgent\HydraAgent.zip" #Invoke-WebRequest -Uri $DownloadAdress -OutFile "$env:ProgramFiles\ITProCloud.de\HydraAgent\HydraAgent.zip" -UseBasicParsing # Stop a running instance LogWriter("Stop a running instance") Stop-ScheduledTask -TaskName 'ITPC-AVD-Hydra-Helper' -ErrorAction Ignore Stop-Process -Name HydraAgent -Force -ErrorAction Ignore Start-Sleep -Seconds 6 UnzipFile "$env:ProgramFiles\ITProCloud.de\HydraAgent\HydraAgent.zip" "$env:ProgramFiles\ITProCloud.de\HydraAgent" # Configuring the agent LogWriter("Configuring the agent") cd "$env:ProgramFiles\ITProCloud.de\HydraAgent" . "$env:ProgramFiles\ITProCloud.de\HydraAgent\HydraAgent.exe" -i -u "wss://$($uri)/wsx" -s $secret } catch { LogWriter("An error occurred while installing Hydra Agent: $_") } } # install AVD Agent if a registration key given if ($WvdRegistrationKey -ne "") { if ([System.Environment]::OSVersion.Version.Major -gt 6) { LogWriter("Installing AVD agent") Start-Process -wait -FilePath "${LocalConfig}\Microsoft.RDInfra.RDAgent.msi" -ArgumentList "/quiet /qn /norestart /passive RegistrationToken=${WvdRegistrationKey}" if ($false) { LogWriter("Installing AVD boot loader - current path is ${LocalConfig}") Start-Process -wait -FilePath "${LocalConfig}\Microsoft.RDInfra.RDAgentBootLoader.msi" -ArgumentList "/quiet /qn /norestart /passive" LogWriter("Waiting for the service RDAgentBootLoader") $bootloaderServiceName = "RDAgentBootLoader" $retryCount = 0 while ( -not (Get-Service "RDAgentBootLoader" -ErrorAction SilentlyContinue)) { $retry = ($retryCount -lt 6) LogWriter("Service RDAgentBootLoader was not found") if ($retry) { LogWriter("Retrying again in 30 seconds, this will be retry $retryCount") } else { LogWriter("Retry limit exceeded" ) throw "RDAgentBootLoader didn't become available after 6 retries" } $retryCount++ Start-Sleep -Seconds 30 } } else { LogWriter("Preparing AVD boot loader task") $action = New-ScheduledTaskAction -Execute "$env:windir\System32\WindowsPowerShell\v1.0\Powershell.exe" -Argument "-executionPolicy Unrestricted -File `"$LocalConfig\ITPC-WVD-Image-Processing.ps1`" -Mode `"RDAgentBootloader`"" $trigger = New-ScheduledTaskTrigger -AtStartup $principal = New-ScheduledTaskPrincipal 'NT Authority\SYSTEM' -RunLevel Highest $settingsSet = New-ScheduledTaskSettingsSet $task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $settingsSet Register-ScheduledTask -TaskName 'ITPC-AVD-RDAgentBootloader-Helper' -InputObject $task -ErrorAction Ignore Enable-ScheduledTask -TaskName 'ITPC-AVD-RDAgentBootloader-Helper' LogWriter("Added new startup task to run the RDAgentBootloader") $class = cimclass MSFT_TaskEventTrigger root/Microsoft/Windows/TaskScheduler $triggerM = $class | New-CimInstance -ClientOnly $triggerM.Enabled = $true $triggerM.Subscription='' $actionM = New-ScheduledTaskAction -Execute "$env:windir\System32\WindowsPowerShell\v1.0\Powershell.exe" -Argument "-executionPolicy Unrestricted -File `"$LocalConfig\ITPC-WVD-Image-Processing.ps1`" -Mode `"RestartBootloader`"" $settingsM = New-ScheduledTaskSettingsSet $taskM = New-ScheduledTask -Action $actionM -Principal $principal -Trigger $triggerM -Settings $settingsM -Description "Restarts the bootloader in case of an known issue (timeout, download error) while installing the RDagent" Register-ScheduledTask -TaskName 'ITPC-AVD-RDAgentBootloader-Monitor-1' -InputObject $taskM -ErrorAction Ignore Enable-ScheduledTask -TaskName 'ITPC-AVD-RDAgentBootloader-Monitor-1' -ErrorAction Ignore LogWriter("Added new task to monitor the RDAgentBootloader") } } else { if ((Test-Path "${LocalConfig}\Microsoft.RDInfra.WVDAgent.msi") -eq $false) { LogWriter("Downloading Microsoft.RDInfra.WVDAgent.msi") DownloadFile "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE3JZCm" "${LocalConfig}\Microsoft.RDInfra.WVDAgent.msi" #Invoke-WebRequest -Uri 'https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE3JZCm' -OutFile "${LocalConfig}\Microsoft.RDInfra.WVDAgent.msi" -UseBasicParsing } if ((Test-Path "${LocalConfig}\Microsoft.RDInfra.WVDAgentManager.msi") -eq $false) { LogWriter("Downloading Microsoft.RDInfra.WVDAgentManager.msi") DownloadFile "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE3K2e3" "${LocalConfig}\Microsoft.RDInfra.WVDAgentManager.msi" #nvoke-WebRequest -Uri 'https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE3K2e3' -OutFile "${LocalConfig}\Microsoft.RDInfra.WVDAgentManager.msi" -UseBasicParsing } LogWriter("Installing AVDAgent") Start-Process -wait -FilePath "${LocalConfig}\Microsoft.RDInfra.WVDAgent.msi" -ArgumentList "/q RegistrationToken=${WvdRegistrationKey}" LogWriter("Installing AVDAgentManager") Start-Process -wait -FilePath "${LocalConfig}\Microsoft.RDInfra.WVDAgentManager.msi" -ArgumentList '/q' } } LogWriter("Enabling ITPC-LogAnalyticAgent and MySmartScale if exist") Enable-ScheduledTask -TaskName "ITPC-LogAnalyticAgent for RDS and Citrix" -ErrorAction Ignore Enable-ScheduledTask -TaskName "ITPC-MySmartScaleAgent" -ErrorAction Ignore if ([System.IO.File]::Exists("C:\ProgramData\Optimize\Win10_VirtualDesktop_Optimize.ps1")) { LogWriter("Running VDI Optimization script") Start-Process -wait -FilePath PowerShell.exe -WorkingDirectory "C:\ProgramData\Optimize" -ArgumentList '-ExecutionPolicy Bypass -File "C:\ProgramData\Optimize\Win10_VirtualDesktop_Optimize.ps1 -AcceptEULA -Optimizations WindowsMediaPlayer,AppxPackages,ScheduledTasks,DefaultUserSettings,Autologgers,Services,NetworkOptimizations"' -RedirectStandardOutput "$($LogDir)\VirtualDesktop_Optimize.Stage2.Out.txt" -RedirectStandardError "$($LogDir)\VirtualDesktop_Optimize.Stage2.Warning.txt" } # Final reboot LogWriter("Finally restarting session host") Restart-Computer -Force -ErrorAction SilentlyContinue } elseif ($Mode -eq "DataPartition") { if ((Get-WmiObject -Class win32_volume | Where-Object { $_.DriveLetter -ne $null -and $_.DriveType -eq 3 }).Count -eq 3) { # change drive letters of temp and data drive for VMs with 3 drives LogWriter("VM with 3 drives so change drive letters of temp and data") ShowDrives # change c:\pagefile.sys to e:\pagefile.sys ShowPageFiles $CurrentPageFile = Get-WmiObject -Query 'select * from Win32_PageFileSetting' if ($null -eq $CurrentPageFile) { LogWriter("No pagefile found") } else { if ($CurrentPageFile.Name.tolower().contains('c:')) { ShowDrives # change temp drive to Z: $drive = Get-WmiObject -Class win32_volume -Filter "DriveLetter = 'd:'" if ($null -ne $drive) { LogWriter("d: drive: $($drive.Label)") Set-WmiInstance -input $drive -Arguments @{ DriveLetter='z:' } LogWriter("changed drive letter to z:") ShowDrives } else { LogWriter("Drive D: not found") } # change data drive to D: $drive = Get-WmiObject -Class win32_volume -Filter "DriveLetter = 'e:'" if ($null -ne $drive) { LogWriter("e: drive: $($drive.Label)") Set-WmiInstance -input $drive -Arguments @{ DriveLetter='D:' } LogWriter("changed drive letter to D:") ShowDrives } else { LogWriter("Drive E: not found") } # change temp drive back to E: $drive = Get-WmiObject -Class win32_volume -Filter "DriveLetter = 'z:'" if ($null -ne $drive) { LogWriter("z: drive: $($drive.Label)") Set-WmiInstance -input $drive -Arguments @{ DriveLetter='E:' } LogWriter("changed drive letter to E:") ShowDrives } else { LogWriter("Drive Z: not found") } # change c:\pagefile.sys to e:\pagefile.sys ShowPageFiles $CurrentPageFile = Get-WmiObject -Query 'select * from Win32_PageFileSetting' if ($null -eq $CurrentPageFile) { LogWriter("No pagefile found") } else { $CurrentPageFile.delete() LogWriter("Old pagefile deleted") } ShowPageFiles Set-WMIInstance -Class Win32_PageFileSetting -Arguments @{name='e:\pagefile.sys';InitialSize = 0; MaximumSize = 0} LogWriter("set pagefile to e:\pagefile.sys") ShowPageFiles # reboot to activate pagefile LogWriter("Finally restarting session host") Restart-Computer -Force LogWriter("After Finally restarting session host") } } } LogWriter("Disable scheduled task") try { # disable startup scheduled task Disable-ScheduledTask -TaskName 'ITPC-AVD-Disk-Mover-Helper' } catch { LogWriter("Disabling scheduled task failed: " + $_.Exception.Message) } } elseif ($Mode -eq "RDAgentBootloader") { LogWriter("Installing AVD boot loader - current path is ${LocalConfig}") Start-Process -wait -FilePath "${LocalConfig}\Microsoft.RDInfra.RDAgentBootLoader.msi" -ArgumentList "/quiet /qn /norestart /passive" LogWriter("Waiting for the service RDAgentBootLoader") $bootloaderServiceName = "RDAgentBootLoader" $retryCount = 0 while ( -not (Get-Service "RDAgentBootLoader" -ErrorAction SilentlyContinue)) { $retry = ($retryCount -lt 6) LogWriter("Service RDAgentBootLoader was not found") if ($retry) { LogWriter("Retrying again in 30 seconds, this will be retry $retryCount") } else { LogWriter("Retry limit exceeded" ) throw "RDAgentBootLoader didn't become available after 6 retries" } $retryCount++ Start-Sleep -Seconds 30 } LogWriter("Disable scheduled task") try { # disable startup scheduled task Disable-ScheduledTask -TaskName 'ITPC-AVD-RDAgentBootloader-Helper' } catch { LogWriter("Disabling scheduled task failed: " + $_.Exception.Message) } } elseif ($Mode -eq "CleanFirstStart") { LogWriter("Cleaning up Azure Agent logs - current path is ${LocalConfig}") Remove-Item -Path "C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\*" -Include *.status -Recurse -Force -ErrorAction SilentlyContinue LogWriter("Disable scheduled task") try { # disable startup scheduled task Disable-ScheduledTask -TaskName 'ITPC-AVD-CleanFirstStart-Helper' } catch { LogWriter("Disabling scheduled task failed: " + $_.Exception.Message) } } elseif ($mode -eq "RestartBootloader") { $LogFile=$LogDir+"\AVD.AgentBootloaderErrorHandling.log" LogWriter "Stopping service" Stop-Service -Name "RDAgentBootLoader" LogWriter "Starting service" Start-Service -Name "RDAgentBootLoader" } elseif ($mode -eq "StartBootloader") { $LogFile=$LogDir+"\AVD.AgentBootloaderErrorHandling.log" LogWriter "Start service was triggered by an event" LogWriter "Waiting for 5 seconds" Start-Sleep -Seconds 5 LogWriter "Starting service" Start-Service -Name "RDAgentBootLoader" LogWriter "Waiting for 60 seconds" Start-Sleep -Seconds 60 LogWriter "Starting service (if not running)" Start-Service -Name "RDAgentBootLoader" LogWriter "Waiting for 60 seconds" Start-Sleep -Seconds 60 LogWriter "Starting service (if not running)" Start-Service -Name "RDAgentBootLoader" } # SIG # Begin signature block # MIIjNAYJKoZIhvcNAQcCoIIjJTCCIyECAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4qJrUnQJN2ErhFHujsk5VW1I # XQyggh1SMIIFEzCCA/ugAwIBAgIQAs5KUttbmmyoHluSw+u3hTANBgkqhkiG9w0B # AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz # c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMTIwNDAwMDAwMFoXDTI0MDEx # ODIzNTk1OVowUDELMAkGA1UEBhMCREUxETAPBgNVBAcTCE9kZW50aGFsMRYwFAYD # VQQKEw1NYXJjZWwgTWV1cmVyMRYwFAYDVQQDEw1NYXJjZWwgTWV1cmVyMIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2DJvVgDXARIQxVyuGvFZDgWsEnEb # YFcDwOrBPdx7vjduyzW2YHMh5Alem02VOT7enx/pjzS4T59xGyDIVb8Nfaq4H3RR # pb/M6gIXNR40rDX0gzx9ZKXB/2M9bA40p/w5B2HVJeWEoU9/1AuJOPPYzljO7fBh # C2nw/WaqwX/jhbyqXy66xlFrsjGd1PBLnsySaSQ+uXnEYQJLg8FVnv/0scDFGt2E # p9cWGQF1kOPoR3VlWa95iaDkX6gYZrv5MEqDdLFoW0WBmaR5Qqa0SsheRInscij7 # JjStV8tOv35SCB3sGH7X1DDN3nFX4ba3hAZ+tW41pmTIX4ZmHDBfwRSN7QIDAQAB # o4IBxTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0O # BBYEFKFMubbYFl0FASOBvmKegL2pwro/MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE # DDAKBggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2Ny # bDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUw # QzA3BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNl # cnQuY29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcw # AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8v # Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNp # Z25pbmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAIUEU # LUWCQqvHhicLHsPM16CRbhWj2SdqOMPHjkODY/MLIDV1guZO+071OguT94DFlmdO # I3MYIEIEW/79XTf90CXmEXlgsnKnSkv3FgcCZuKAGFHgg8vSp+dWlMthI4XS4kQ1 # I02igEYnzTif3HzSffODz6xi3QzGDspSgRM4tEVej7xLyKys/r/TZggXjc6RFZfr # oq0D8c7lUd+cwPuHo57YVXZKWS2sJaZua05C2NmO5+4WrMjbeltBrhh8Vdsx40BG # iiE/W5/SWSyFIK+Dw9b9zIeDZgS58uJmF1C5XrwC/XX9pCBz91Dbz+CfkTPBv3T3 # wBsnkoJfNCSB2WHg9zCCBTAwggQYoAMCAQICEAQJGBtf1btmdVNDtW+VUAgwDQYJ # KoZIhvcNAQELBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu # YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQg # QXNzdXJlZCBJRCBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAw # MFowcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE # CxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1 # cmVkIElEIENvZGUgU2lnbmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC # AQoCggEBAPjTsxx/DhGvZ3cH0wsxSRnP0PtFmbE620T1f+Wondsy13Hqdp0FLreP # +pJDwKX5idQ3Gde2qvCchqXYJawOeSg6funRZ9PG+yknx9N7I5TkkSOWkHeC+aGE # I2YSVDNQdLEoJrskacLCUvIUZ4qJRdQtoaPpiCwgla4cSocI3wz14k1gGL6qxLKu # cDFmM3E+rHCiq85/6XzLkqHlOzEcz+ryCuRXu0q16XTmK/5sy350OTYNkO/ktU6k # qepqCquE86xnTrXE94zRICUj6whkPlKWwfIPEvTFjg/BougsUfdzvL2FsWKDc0GC # B+Q4i2pzINAPZHM8np+mM6n9Gd8lk9ECAwEAAaOCAc0wggHJMBIGA1UdEwEB/wQI # MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHkG # CCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu # Y29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln # aUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRw # Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3Js # MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk # SURSb290Q0EuY3JsME8GA1UdIARIMEYwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUF # BwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAoGCGCGSAGG/WwDMB0G # A1UdDgQWBBRaxLl7KgqjpepxA8Bg+S32ZXUOWDAfBgNVHSMEGDAWgBRF66Kv9JLL # gjEtUYunpyGd823IDzANBgkqhkiG9w0BAQsFAAOCAQEAPuwNWiSz8yLRFcgsfCUp # dqgdXRwtOhrE7zBh134LYP3DPQ/Er4v97yrfIFU3sOH20ZJ1D1G0bqWOWuJeJIFO # EKTuP3GOYw4TS63XX0R58zYUBor3nEZOXP+QsRsHDpEV+7qvtVHCjSSuJMbHJyqh # KSgaOnEoAjwukaPAJRHinBRHoXpoaK+bp1wgXNlxsQyPu6j4xRJon89Ay0BEpRPw # 5mQMJQhCMrI2iiQC/i9yfhzXSUWW6Fkd6fp0ZGuy62ZD2rOwjNXpDd32ASDOmTFj # PQgaGLOBm0/GkxAG/AeB+ova+YJJ92JuoVP6EpQYhS6SkepobEQysmah5xikmmRR # 7zCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAw # ZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBS # b290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjAN # BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUu # ySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8 # Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0M # G+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldX # n1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVq # GDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFE # mjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6 # SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXf # SwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b23 # 5kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ # 6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRp # L5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O # BBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1R # i6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYB # BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0 # cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v # RGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADAN # BgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVe # qRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3vot # Vs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum # 6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJ # aISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ # ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDCCBq4wggSWoAMCAQIC # EAc2N7ckVHzYR6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMx # FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv # bTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAw # MDAwMFoXDTM3MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp # Z2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQw # OTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP # ADCCAgoCggIBAMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2 # EaFEFUJfpIjzaPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuA # hIoiGN/r2j3EF3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQ # h0YAe9tEQYncfGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7Le # Sn3O9TkSZ+8OpWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw5 # 4qVI1vCwMROpVymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP2 # 9p7mO1vsgd4iFNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjF # KfPKqpZzQmiftkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHt # Qr8FnGZJUlD0UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpY # PtMDiP6zj9NeS3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4J # duyrXUZ14mCjWAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGj # ggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2 # mi91jGogj57IbzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNV # HQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBp # MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUH # MAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRS # b290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EM # AQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIB # fmbW2CFC4bAYLhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb # 122H+oQgJTQxZ822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+r # T4osequFzUNf7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQ # sl3p/yhUifDVinF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsK # RcnfxI2g55j7+6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKn # N36TU6w7HQhJD5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSe # reU0cZLXJmvkOHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no # 8Zhf+yvYfvJGnXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcW # oWa63VXAOimGsJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInw # AM1dwvnQI38AC+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7 # qS9EFUrnEw4d2zc4GqEr9u3WfPwwggbAMIIEqKADAgECAhAMTWlyS5T6PCpKPSkH # gD1aMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp # Q2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2 # IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjIwOTIxMDAwMDAwWhcNMzMxMTIx # MjM1OTU5WjBGMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQxJDAiBgNV # BAMTG0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIyIC0gMjCCAiIwDQYJKoZIhvcNAQEB # BQADggIPADCCAgoCggIBAM/spSY6xqnya7uNwQ2a26HoFIV0MxomrNAcVR4eNm28 # klUMYfSdCXc9FZYIL2tkpP0GgxbXkZI4HDEClvtysZc6Va8z7GGK6aYo25BjXL2J # U+A6LYyHQq4mpOS7eHi5ehbhVsbAumRTuyoW51BIu4hpDIjG8b7gL307scpTjUCD # HufLckkoHkyAHoVW54Xt8mG8qjoHffarbuVm3eJc9S/tjdRNlYRo44DLannR0hCR # RinrPibytIzNTLlmyLuqUDgN5YyUXRlav/V7QG5vFqianJVHhoV5PgxeZowaCiS+ # nKrSnLb3T254xCg/oxwPUAY3ugjZNaa1Htp4WB056PhMkRCWfk3h3cKtpX74LRsf # 7CtGGKMZ9jn39cFPcS6JAxGiS7uYv/pP5Hs27wZE5FX/NurlfDHn88JSxOYWe1p+ # pSVz28BqmSEtY+VZ9U0vkB8nt9KrFOU4ZodRCGv7U0M50GT6Vs/g9ArmFG1keLuY # /ZTDcyHzL8IuINeBrNPxB9ThvdldS24xlCmL5kGkZZTAWOXlLimQprdhZPrZIGwY # UWC6poEPCSVT8b876asHDmoHOWIZydaFfxPZjXnPYsXs4Xu5zGcTB5rBeO3GiMiw # bjJ5xwtZg43G7vUsfHuOy2SJ8bHEuOdTXl9V0n0ZKVkDTvpd6kVzHIR+187i1Dp3 # AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNV # HSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgB # hv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYE # FGKK3tBh/I8xFO2XC809KpQU31KcMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9j # cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZU # aW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzAB # hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9j # YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEy # NTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAFWqKhrzRvN4 # Vzcw/HXjT9aFI/H8+ZU5myXm93KKmMN31GT8Ffs2wklRLHiIY1UJRjkA/GnUypsp # +6M/wMkAmxMdsJiJ3HjyzXyFzVOdr2LiYWajFCpFh0qYQitQ/Bu1nggwCfrkLdcJ # iXn5CeaIzn0buGqim8FTYAnoo7id160fHLjsmEHw9g6A++T/350Qp+sAul9Kjxo6 # UrTqvwlJFTU2WZoPVNKyG39+XgmtdlSKdG3K0gVnK3br/5iyJpU4GYhEFOUKWaJr # 5yI+RCHSPxzAm+18SLLYkgyRTzxmlK9dAlPrnuKe5NMfhgFknADC6Vp0dQ094XmI # vxwBl8kZI4DXNlpflhaxYwzGRkA7zl011Fk+Q5oYrsPJy8P7mxNfarXH4PMFw1nf # J2Ir3kHJU7n/NBBn9iYymHv+XEKUgZSCnawKi8ZLFUrTmJBFYDOA4CPe+AOk9kVH # 5c64A0JH6EE2cXet/aLol3ROLtoeHYxayB6a1cLwxiKoT5u92ByaUcQvmvZfpyeX # upYuhVfAYOd4Vn9q78KVmksRAsiCnMkaBXy6cbVOepls9Oie1FqYyJ+/jbsYXEP1 # 0Cro4mLueATbvdH7WwqocH7wl4R44wgDXUcsY6glOJcB0j862uXl9uab3H4szP8X # TE0AotjWAQ64i+7m4HJViSwnGWH2dwGMMYIFTDCCBUgCAQEwgYYwcjELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg # U2lnbmluZyBDQQIQAs5KUttbmmyoHluSw+u3hTAJBgUrDgMCGgUAoHgwGAYKKwYB # BAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAc # BgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQU9eeb # 6SzZ/8wr7lCRfzqDfWfZr2YwDQYJKoZIhvcNAQEBBQAEggEADIxucTzcaC9NWiF6 # r8tIqbCMTy5jA/kvCDHMtzPsu+48EfFCgetnmynpNgVH+3Z4/kDVC0tXemwkzmQV # +75LTfDzGG4NA9sbJe47IGjOLRzi1onZ9uxcq8Bntmwcoz4DWUsmq+DHT1DuAy5y # vYAPSmDo1ZpVxWy9t7tBkPPqJ0h6SON7P/VxeZMHBw6bG8876SOYgNF2KwQHMoC3 # ERzHUZ1Kcm1WJsw5X9s09UaSDsrJb4eACCZgD7vNf/bxweZFn59Y8hCcvOTnSyIC # A09DRJoNut4Iw45xTz1ELPcbC0lmp3epf4rs6xHddNb6XVpfy0EZKv5iJdmAz0YW # uXSFYKGCAyAwggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYT # AlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQg # VHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAxNaXJL # lPo8Kko9KQeAPVowDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZI # hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMzAzMjgwODEyNTlaMC8GCSqGSIb3DQEJ # BDEiBCAR/prLtqgX21MjxS5xrBDA/0n5jM8kZmX2PyMiAiUZ+jANBgkqhkiG9w0B # AQEFAASCAgBJ4FEhMbXx5f67nYtrJMbZYDp02X+KjPbIIDV7JDojESWKncyJQ9HP # qCgie6dV+3VQitBntrYtG4kPQAqbD+ZPKowNuDAr3WnQMCgi1R/OvcIeEhaQEvyA # ExFXhrW7uwBtpP6JdFAH/VyrYLwKO39qwbaP0abz+T/dNjwdaI5i30P5YgFlEnr1 # rm3jpYOgwr/H73fcBhVfOvGGsHq0d28BmJcW7vtY6e+wrzoaQyK9PO/feHs8kgwo # iTzqkg8hA4Zl7uNDBhpqMWGcii6kXtYasKwNO5Tp3hcqfK07vH6JYGK7Exu5SloV # iD5UGOmnhreyxJaGpx0s8FyNewqJHMTkzDpYiPOvZryD88TciNbjTUAoo9LlCXTE # l2ADFoNzIqkA0cjuBkFDGVAPqdnJ+aiUArdlkx9mhIP8l9rhFJRBvjoBIez0l/J/ # VoqILQOL5T9uFkiGD74Tt2tCRzzL423+KNmmZs8oagEP9Enc9ezmouJcCKmw9Jn3 # +HzuS/GnVEpKTM365bAeGPgEK+ujc7PIfmVos4+oDxngAi/t7EGEc+mOpPcp8PkL # ST/Q06U6Y/v2g6tE6blOajAsjahkuUnjZOiFqyfaf2piWLX7C77lUsg2TFxVwvSi # gMoSj3+IP+M+OAkaxq6YqGKRjLbxITnfSlevn6iL37S0qvAl4c+0XA== # SIG # End signature block