# 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 # MIIjNgYJKoZIhvcNAQcCoIIjJzCCIyMCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4qJrUnQJN2ErhFHujsk5VW1I # XQyggh1UMIIFEzCCA/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 # qS9EFUrnEw4d2zc4GqEr9u3WfPwwggbCMIIEqqADAgECAhAFRK/zlJ0IOaa/2z9f # 5WEWMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp # Q2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2 # IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjMwNzE0MDAwMDAwWhcNMzQxMDEz # MjM1OTU5WjBIMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x # IDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIzMIICIjANBgkqhkiG9w0B # AQEFAAOCAg8AMIICCgKCAgEAo1NFhx2DjlusPlSzI+DPn9fl0uddoQ4J3C9Io5d6 # OyqcZ9xiFVjBqZMRp82qsmrdECmKHmJjadNYnDVxvzqX65RQjxwg6seaOy+WZuNp # 52n+W8PWKyAcwZeUtKVQgfLPywemMGjKg0La/H8JJJSkghraarrYO8pd3hkYhftF # 6g1hbJ3+cV7EBpo88MUueQ8bZlLjyNY+X9pD04T10Mf2SC1eRXWWdf7dEKEbg8G4 # 5lKVtUfXeCk5a+B4WZfjRCtK1ZXO7wgX6oJkTf8j48qG7rSkIWRw69XloNpjsy7p # Be6q9iT1HbybHLK3X9/w7nZ9MZllR1WdSiQvrCuXvp/k/XtzPjLuUjT71Lvr1KAs # NJvj3m5kGQc3AZEPHLVRzapMZoOIaGK7vEEbeBlt5NkP4FhB+9ixLOFRr7StFQYU # 6mIIE9NpHnxkTZ0P387RXoyqq1AVybPKvNfEO2hEo6U7Qv1zfe7dCv95NBB+plwK # WEwAPoVpdceDZNZ1zY8SdlalJPrXxGshuugfNJgvOuprAbD3+yqG7HtSOKmYCaFx # smxxrz64b5bV4RAT/mFHCoz+8LbH1cfebCTwv0KCyqBxPZySkwS0aXAnDU+3tTbR # yV8IpHCj7ArxES5k4MsiK8rxKBMhSVF+BmbTO77665E42FEHypS34lCh8zrTioPL # QHsCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG # A1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCG # SAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4E # FgQUpbbvE+fvzdBkodVWqWUxo97V40kwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov # L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1 # NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUH # MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDov # L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNI # QTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAgRrW3qCp # tZgXvHCNT4o8aJzYJf/LLOTN6l0ikuyMIgKpuM+AqNnn48XtJoKKcS8Y3U623mzX # 4WCcK+3tPUiOuGu6fF29wmE3aEl3o+uQqhLXJ4Xzjh6S2sJAOJ9dyKAuJXglnSoF # eoQpmLZXeY/bJlYrsPOnvTcM2Jh2T1a5UsK2nTipgedtQVyMadG5K8TGe8+c+nji # kxp2oml101DkRBK+IA2eqUTQ+OVJdwhaIcW0z5iVGlS6ubzBaRm6zxbygzc0brBB # Jt3eWpdPM43UjXd9dUWhpVgmagNF3tlQtVCMr1a9TMXhRsUo063nQwBw3syYnhmJ # A+rUkTfvTVLzyWAhxFZH7doRS4wyw4jmWOK22z75X7BC1o/jF5HRqsBV44a/rCcs # QdCaM0qoNtS5cpZ+l3k4SF/Kwtw9Mt911jZnWon49qfH5U81PAC9vpwqbHkB3NpE # 5jreODsHXjlY9HxzMVWggBHLFAx+rrz+pOt5Zapo1iLKO+uagjVXKBbLafIymrLS # 2Dq4sUaGa7oX/cR3bBVsrquvczroSUa31X/MtjjA2Owc9bahuEMs305MfR5ocMB3 # CtQC4Fxguyj/OOVSWtasFyIjTvTs0xf7UGv/B3cfcZdEQcm4RtNsMnxYL2dHZeUb # c7aZ+WssBkbvQR7w8F/g29mtkIBEr4AQQYoxggVMMIIFSAIBATCBhjByMQswCQYD # VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln # aWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29k # ZSBTaWduaW5nIENBAhACzkpS21uabKgeW5LD67eFMAkGBSsOAwIaBQCgeDAYBgor # BgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEE # MBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBT1 # 55vpLNn/zCvuUJF/OoN9Z9mvZjANBgkqhkiG9w0BAQEFAASCAQAMjG5xPNxoL01a # IXqvy0ipsIxPLmMD+S8IMcy3M+y77jwR8UKB62ebKek2BUf7dnj+QNULS1d6bCTO # ZBX7vktN8PMYbg0D2xsl7jsgaM4tHOLWidn27FyrwGe2bByjPgNZSyar4MdPUO4D # LnK9gA9KYOjVmlXFbL23u0GQ8+onSHpI43s/9XF5kwcHDpsbzzvpI5iA0XYrBAcy # gLcRHMdRnUpybVYmzDlf2zT1RpIOyslvh4AIJmAPu81/9vHB5kWfn1jyEJy85OdL # IgIDT0NEmg263gjDjnFPPUQs9xsLSWand6l/iuzrEd101vpdWl/LQRkq/mIl2YDP # Rha5dIVgoYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2Vy # dCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQBUSv # 85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkq # hkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIzMDkxOTE3NTkyOVowLwYJKoZIhvcN # AQkEMSIEIBH+msu2qBfbUyPFLnGsEMD/SfmMzyRmZfY/IyICJRn6MA0GCSqGSIb3 # DQEBAQUABIICAGTqvaUjW0lqdA1d29M9+nz8BdDCS1hZrhoJRw7ndGZyP7CNz2Z/ # vMIVNHlRLOUSDymY+ca5hLnuvbmA8fTrJsAQVR2GtBWAojGvn7Ou2IFGqZcyuDJ5 # Ola6qY7yse1QP3SDKgWXScnIhkLJYzqoiEYpUZ+RFX/VVbHe21Dvo0vC7e+5+vuo # g6QhEpEA4Q+SjQnrrHDyazMbM6ZIXskur+n3ngBPKXeSL3BUPqUYlqTmk3MerFwr # BbWnUNiikrM1Q9IQ8OqfegxEsFzu0g0z9XsfCOvZHOaGoJvOA6QVOCeAzjTRXzsL # L6Qkv+hhh5/CtdK6QBjLrz1JsCc1ZVK/B9XfcsGO39ANhj7ECZvXAu5z+DncnXAZ # /WeB4npm24X1/Uw1EgSC+oiEBovNqtbsakRNDFX/D7P1qjT8ZMnCY8PM2WV9wblK # 8E8AYIRoWaZ6YwqNP1Dqgh5HI4tMNYt/hO2pGlfMNx/oehx1myzgVuSuqpXPWJE3 # lygoNog6i/qqIpcVjLCCcY1iAFHvLtOwXdADajNXmkVGhKCZnCLz5YmC7Cocsf1u # hlNgfM9ibCpZtfFEMqEZNWkgIOzJ8TTV0Vdlvk0yu203MFmx04i6LGpcojcFPEmC # IG5fX+d2uFIzVIz3pFQNcreJRpMun1O4E5zXZ8Rn+141OJ0YB9p4yxtZ # SIG # End signature block