Jump to content
Welcome to our new Citrix community!
  • 0

How to automate unattended VDA upgrade


Marco Michalski

Question

Hi guys,

 

we are facing a challenge of establishing automated and unattended process to do the upgrade of the VDA version. Since we have around 50 VMs we cannot check every VDA every three months. So it should basically happen overnight and it can do as many reboots as it needs but it should not require any user interaction so that in the morning the new VDA is installed and registered and ready to serve the next connections.

We tried several approaches to do the upgrade but all required user interactions after the reboot.

How do you guys approach this topic, since the release cycle is very close, and no one wants to click though hundreds of VDAs.

 

Thank you in advance,

Marco

Link to comment

8 answers to this question

Recommended Posts

  • 1

@Carl: That is an awesome link as well. I hadn't seen that before (appears it was posted recently). 

Here is my entire, cleaned up, script. We push this using our deployment software as needed. 

 

#***************************************************************
#***************************************************************
# XenApp 6 Uninstallation and 7.xx Installation Script  
# 
#
#
# Directions:
# Script to be run as administrator from console session.
#
# Before running script perform the following:
# Set-ExecutionPolicy Unrestricted -Force
#
# Script will prompt for credentials to execute under
#
# Script can be started at any particular step by adding number
# to the execution. EG:
# .\XenApp-Upgrade 1 (begins at step 1)
# .\XenApp-Upgrade 3 (begins at step 3)
# etc.
#
# Script will log all operations to a XenAppUpgrade.log in 
# C:\Company.
#
# Date: -----
# Author: -----
# References: Citrix XenApp 6.0 > 6.5 Upgrade Scripting
#
# NOTE: DO NOT DEPLOY THIS SCRIPT WITH KACE WITHOUT MODIFICATION
#***************************************************************
#***************************************************************

param ([int] $NextStep) 

#***************************************************************
#***************************************************************
# Global variable definitions
#***************************************************************
#***************************************************************
#Figures out which version of the OS you are running and runs the corresponding Powershell Version
if ($env:PROCESSOR_ARCHITEW6432 -eq "AMD64") {
    write-warning "changing from 32bit to 64bit PowerShell..."
    $powershell=$PSHOME.tolower().replace("syswow64","sysnative").replace("system32","sysnative")

    if ($myInvocation.Line) {
        &"$powershell\powershell.exe" -NonInteractive -NoProfile $myInvocation.Line
    } else {
        &"$powershell\powershell.exe" -NonInteractive -NoProfile -file "$($myInvocation.InvocationName)" $args
    }

    exit $lastexitcode
}

# Registry Key details for storing this scripts state information
$global:ScriptStateInfoKey = "HKLM:\software\Company"
$global:ScriptStateInfoName = "ScriptStatus"
$global:ScriptStateLastExecutionDate = "LastExecutionDate"
$global:ScriptStateLastExecutionCommandLine = "LastExecutionCommandLine"
$global:ScriptStateLastExecutionStep1 = "LastExecutionStep1"
$global:ScriptStateLastExecutionStep2 = "LastExecutionStep2"
$global:ScriptStateLastExecutionStep3 = "LastExecutionStep3"
$global:ScriptStateLastExecutionStep4 = "LastExecutionStep4"
$global:ScriptStateLastExecutionStep5 = "LastExecutionStep5"
$global:ScriptStateNewInstallName = "NewInstall"
$global:BackupDUN = "DefaultUserName"
$global:BackupPW  = "DefaultPassword"
$global:BackupAAL = "AutoAdminLogon"

$global:ScriptWinlogon = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
$global:ScriptDefaultUserName = "DefaultUserName"
$global:ScriptDefaultPassword = "DefaultPassword"
$global:ScriptAutoAdminLogon = "AutoAdminLogon"

# Taskname used to identify Scheduled Task.  Defaults to the name of the script itself.  
$global:taskname = $myinvocation.mycommand.definition -replace '\W','_'

# Log information
$global:logfolder = (Get-ChildItem env:systemdrive).Value + "\Contoso"
$global:logfile = "$($global:LogFolder)\XenAppUpgrade.Log"

# Upgrade File Location
$global:UpgradeFiles = "C:\Contoso\XenApp-Upgrade"

# Command executed, used for displaying command line history
[string] $global:commandline = $myinvocation.mycommand.definition + " " + $NextStep

# Domain\User used to execute this script
# Domain and Username combined
if ((get-childitem env:userdomain).Value -ne "")
	{
	$global:TaskUserName = (get-childitem env:userdomain).Value + "\" + (get-childitem env:username).Value
	}
	else
	{
	$global:TaskUserName = (get-childitem env:username).Value
	}

#$SecureTaskUserPW = Read-Host "Please enter the run as password for $($global:TaskUserName)" -AsSecureString
#$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureTaskUserPW)
#$global:TaskUserPW = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

$global:TaskUserName = "CONTOSO\adminusername"
$global:TaskUserPW = "somepassword"
$tpw = ConvertTo-SecureString $global:TaskUserPW -AsPlainText -Force
$global:Cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $global:TaskUserName, $tpw

# Flag to determine if upgrading to version 7.15 from a 7.x version. Used to skip unneeded registry modification.
$global:seven = $false

# Flag to determine if fresh installation of 7.15. Used to skip EUEM take ownership process.
$global:FreshInstall = $false

#Mail Server information
$global:SMTPServer = "smtp.CONTOSO.com"
$global:FromAddress = "no-reply@CONTOSO.com"
$global:ToAddress = "person@CONTOSO.com"

#Path where the CheckPoint Monitor service will look for checkpoints needing created. The trailing Backslash is required.
$global:CPPath = "\\PCNAME\CheckPointMonitor$\"


#***************************************************************
#***************************************************************
# FUNCTION DEFINITIONS
#***************************************************************
#***************************************************************

function Take-Permissions {
    # Developed for PowerShell v4.0
    # Required Admin privileges
    # Links:
    #   http://shrekpoint.blogspot.ru/2012/08/taking-ownership-of-dcom-registry.html
    #   http://www.remkoweijnen.nl/blog/2012/01/16/take-ownership-of-a-registry-key-in-powershell/
    #   https://powertoe.wordpress.com/2010/08/28/controlling-registry-acl-permissions-with-powershell/

    param($rootKey, $key, [System.Security.Principal.SecurityIdentifier]$sid = 'S-1-5-32-545', $recurse = $true)

#Depending on $rootkey it will set a different regkey path
    switch -regex ($rootKey) {
        'HKCU|HKEY_CURRENT_USER'    { $rootKey = 'CurrentUser' }
        'HKLM|HKEY_LOCAL_MACHINE'   { $rootKey = 'LocalMachine' }
        'HKCR|HKEY_CLASSES_ROOT'    { $rootKey = 'ClassesRoot' }
        'HKCC|HKEY_CURRENT_CONFIG'  { $rootKey = 'CurrentConfig' }
        'HKU|HKEY_USERS'            { $rootKey = 'Users' }
    }

    ### Step 1 - escalate current process's privilege
    # get SeTakeOwnership, SeBackup and SeRestore privileges before executes next lines, script needs Admin privilege
    $import = '[DllImport("ntdll.dll")] public static extern int RtlAdjustPrivilege(ulong a, bool b, bool c, ref bool d);'
    $ntdll = Add-Type -Member $import -Name NtDll -PassThru
    $privileges = @{ SeTakeOwnership = 9; SeBackup =  17; SeRestore = 18 }
    foreach ($i in $privileges.Values) {
        $null = $ntdll::RtlAdjustPrivilege($i, 1, 0, [ref]0)
    }

    function Take-KeyPermissions {
        param($rootKey, $key, $sid, $recurse, $recurseLevel = 0)

        ### Step 2 - get ownerships of key - it works only for current key
        $regKey = [Microsoft.Win32.Registry]::$rootKey.OpenSubKey($key, 'ReadWriteSubTree', 'TakeOwnership')
        $acl = New-Object System.Security.AccessControl.RegistrySecurity
        $acl.SetOwner($sid)
        $regKey.SetAccessControl($acl)

        ### Step 3 - enable inheritance of permissions (not ownership) for current key from parent
        if ($recurseLevel -ne 0) {
            $acl.SetAccessRuleProtection($false, $false)
            $regKey.SetAccessControl($acl)
        }

        ### Step 4 - only for top-level key, change permissions for current key and propagate it for subkeys
        # to enable propagations for subkeys, it needs to execute Steps 2-3 for each subkey (Step 5)
        if ($recurseLevel -eq 0) {
            $regKey = $regKey.OpenSubKey('', 'ReadWriteSubTree', 'ChangePermissions')
            #$rule = New-Object System.Security.AccessControl.RegistryAccessRule($sid, 'FullControl', 'ContainerInherit', 'None', 'Allow')
            $rule = New-Object System.Security.AccessControl.RegistryAccessRule($sid, 'FullControl', 'Allow')
            $acl.SetAccessRule($rule)
            $regKey.SetAccessControl($acl)
        }

        ### Step 5 - recursively repeat steps 2-5 for subkeys
        if ($recurse) {
            foreach($subKey in $regKey.OpenSubKey('').GetSubKeyNames()) {
                Take-KeyPermissions $rootKey ($key+'\'+$subKey) $sid $recurse ($recurseLevel+1)
            }
        }
    }
    Take-KeyPermissions $rootKey $key $sid $recurse
}

function UninstallPackage([string]$ThisGUID, [string] $InformalName)
    {
       # ATTEMPT TO FIND THE INSTALLER PACKAGE
       $a = Get-WmiObject -Class 'Win32_product' | Where-Object {$_.Name -match $InformalName}
       $ProperGUID = $a.IdentifyingNumber

       if ($a -ne $null)
       {
	       $ThisFilePath = "msiexec.exe"
	       $ThisArgumentList = "/x $($ProperGUID) /qn /norestart REBOOT=ReallySuppress"
	       $Message = "Uninstalling $($a.Name)..."; LogMessage $Message
	   
	       $return = (start-process -FilePath $ThisFilePath -argumentlist $ThisArgumentList -wait -passthru).ExitCode
	   
	       if(($return -eq 0) -or ($return -eq 3010))	   
	       {
	       $Message = "SUCCESS - Return Value: $($return)"; LogMessage $Message
	       $Message = ""; LogMessage $Message
	       }
	       else
	       {
	       $Message = "WARNING - Return Value: $($return)"; LogMessage $Message
	       $Message = ""; LogMessage $Message   	   
	       }
	       Return $return
       }
       else
       {
	       # WRITE NOT FOUND ERROR ONLY TO LOG
	       $TimeStamp = get-date
	       $Message = "$($TimeStemp)  $($InformalName) - $($ThisGUID) not found, Continuing..."; LogMessage $Message
	      # Out-File -FilePath $global:logfile -Append -InputObject $Message
	       Return -1
       }
    }
<#
function ScheduleUninstallPackage([string]$ThisGUID, [string] $InformalName, [string]$Thistaskname, [string]$ThisDelay)
    {
       # ATTEMPT TO FIND THE INSTALLER PACKAGE
       $a = Get-WmiObject -Class 'Win32_product' | Where-Object {$_.Name -match $InformalName}
       $ProperGUID = $a.IdentifyingNumber

       if ($a -ne $null)
       {
	       $ThisFilePath = "msiexec.exe"
	       $ThisArgumentList = "/x $($ProperGUID) /qn /norestart REBOOT=ReallySuppress"
	       $Message = "Scheduling $($a.Name)..."; LogMessage $Message
	   
            # DELETE THE SCHEDULED TASK IF SOMEHOW IT IS ALREADY RUNNING
            if(IsTaskScheduled($Thistaskname))
        	    {
		    	    SCHTASKS /DELETE /TN "$($Thistaskname)" /F
                }

		    SCHTASKS /create /RL HIGHEST /tn "$($Thistaskname)" /tr "$($ThisFilePath) $($ThisArgumentList) >> $($global:LogFolder)\$($Thistaskname).LOG" /sc onstart /ru "$($global:TaskUserName)" /rp $global:TaskUserPW /z /delay $ThisDelay /v1

            # VERIFY THAT THE TASK IS SCHEDULED PROPERLY.  IF NOT LOG AN ERROR AND STOP
            if((IsTaskScheduled($Thistaskname)))
                {
                    $Message = "SUCCESS - Task $($Thistaskname) scheduled."; LogMessage $Message
                }
                Else
        	    {
		            $Message = "Failed to schedule the task.  Halting..."; LogMessage $Message
            	    Exit
                }
	       Return 1
       }
       else
       {
	       # WRITE NOT FOUND ERROR ONLY TO LOG
	       $TimeStamp = get-date
	       $Message = "$($TimeStemp)  $($InformalName) - $($ThisGUID) not found, Continuing...";
	       Out-File -FilePath $global:logfile -Append -InputObject $Message
	       Return -1
       }
    }
#>
function UninstallPackage2([string]$ThisGUID, [string] $InformalName)
    {
       # ATTEMPT TO FIND THE INSTALLER PACKAGE
       $a = Get-WmiObject -Class 'Win32_product' | Where-Object {$_.IdentifyingNumber -match $ThisGUID}

       if ($a -ne $null)
       {
	       $ThisFilePath = "msiexec.exe"
	       $ThisArgumentList = "/x $($ThisGUID) /qn /norestart REBOOT=ReallySuppress"
	       $Message = "Uninstalling $($a.Name)..."; LogMessage $Message
	   
	       $return = (start-process -FilePath $ThisFilePath -argumentlist $ThisArgumentList -wait -passthru).ExitCode
	   
	       if(($return -eq 0) -or ($return -eq 3010))	   
	       {
	       $Message = "SUCCESS - Return Value: $($return)"; LogMessage $Message
	       $Message = ""; LogMessage $Message
	       }
	       else
	       {
	       $Message = "WARNING - Return Value: $($return)"; LogMessage $Message
	       $Message = ""; LogMessage $Message   	   
	       }
	       Return $return
       }
       else
       {
	       # WRITE NOT FOUND ERROR ONLY TO LOG
	       $TimeStamp = get-date
	       $Message = "$($TimeStemp)  $($InformalName) - $($ThisGUID) not found, Continuing...";
	       Out-File -FilePath $global:logfile -Append -InputObject $Message
	       Return -1
       }
    }

function UninstallHotFix([string]$ThisProductCode, [string]$ThisPatchCode, [string]$ThisHotFixName)
    {
       if ($ThisProductCode -ne $null)
       {
	       $ThisFilePath = "msiexec.exe"
	       $ThisArgumentList = "/uninstall $($ThisPatchCode) /package $($ThisProductCode) /qn /norestart REBOOT=ReallySuppress"
	       $Message = "Uninstalling $($ThisHotFixName)..."; LogMessage $Message
	   
	       $return = (start-process -FilePath $ThisFilePath -argumentlist $ThisArgumentList -wait -passthru).ExitCode
	   
	       if(($return -eq 0) -or ($return -eq 3010))	   
	       {
	       $Message = "SUCCESS - Return Value: $($return)"; LogMessage $Message
	       $Message = ""; LogMessage $Message
	       }
	       else
	       {
	       $Message = "WARNING - Return Value: $($return)"; LogMessage $Message
	       $Message = ""; LogMessage $Message   	   
	       }
	       Return $return
       }
       else
       {
	       # WRITE NOT FOUND ERROR ONLY TO LOG
	       $TimeStamp = get-date
	       $Message = "$($TimeStemp)  $($ThisHotFixName) - $($ThisPatchCode) not found, Continuing...";
	       Out-File -FilePath $global:logfile -Append -InputObject $Message
	       Return -1
       }
    }

function InstallPatch([string] $PatchName)
    {
	   $ThisFilePath = "msiexec.exe"
	   $ThisArgumentList = "/p $($PatchName) /qn /norestart REBOOT=ReallySuppress"
	   $Message = "Installing patch $($PatchName)..."; LogMessage $Message
	   
	   $return = (start-process -FilePath $ThisFilePath -argumentlist $ThisArgumentList -wait -passthru).ExitCode
	   
	   if(($return -eq 0) -or ($return -eq 3010))	   
	   {
	   $Message = "SUCCESS - Return Value: $($return)"
	   $Message = ""
	   }
	   else
	   {
	   $Message = "WARNING - Return Value: $($return)"
	   $Message = ""   	   
	   }
	   Return $return

    }

function Add-LowRiskFiles()
    {
    $Lowriskregpath ="HKCU:\Software\Microsoft\Windows\Currentversion\Policies\Associations"
    $Lowriskregfile = "LowRiskFileTypes"
    $LowRiskFileTypes = ".exe;.msp;.msi"
    New-Item -Path $Lowriskregpath -erroraction silentlycontinue |out-null
    New-ItemProperty $Lowriskregpath -name $Lowriskregfile -value $LowRiskFileTypes -propertyType String -erroraction silentlycontinue |out-null
    }

function Remove-LowRiskFiles()
    {
    $Lowriskregpath ="HKCU:\Software\Microsoft\Windows\Currentversion\Policies\Associations"
    $Lowriskregfile = "LowRiskFileTypes"
    $LowRiskFileTypes = ".exe;.msi;.msp"
    Remove-ItemProperty -path $Lowriskregpath -name $Lowriskregfile -erroraction silentlycontinue
    }

function IsTaskScheduled ([string] $taskname)
    {
         $ST = new-object -com("Schedule.Service")
         $ST.connect("localhost")
         $RootFolder = $ST.getfolder("\")
         $ScheduledTasks = $RootFolder.GetTasks(0)
         $return = $ScheduledTasks | ? {$_.Name.contains($($taskname))}
         if($return -eq $null)
         {
            return $false
         }
         else
         {
            return $true
         }
    }

function TaskCleanup
    {
            $myvalue = 99
            SetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName $myvalue "DWORD"

            $Message = "Modifying Script Step to: $($myvalue)"; LogMessage $Message

            $Message = "DELETING SCHEDULED TASK, TaskName = $($global:taskname), RETURN TO IDLE (No Operation)"; LogMessage $Message

            # DELETE THE SCHEDULED TASK
            if(IsTaskScheduled($global:taskname))
            {
	         SCHTASKS /DELETE /TN "$($global:taskname)" /F
            }
    }


function LogMessage ([string] $Message)
    {
            [string] $TimeStamp = get-date
		    $Message = $TimeStamp + "  " + $Message
		    write-host $Message
		    out-file -FilePath $global:logfile -Append -InputObject $Message
    }

function LogMessageQuiet ([string] $Message)
    {
            [string] $TimeStamp = get-date
		    $Message = $TimeStamp + "  " + $Message
		    out-file -FilePath $global:logfile -Append -InputObject $Message
    }

function GetRegValue([string]$Key, [string]$Name)
    {
       if(Test-Path $Key)
       {
	       $a = get-itemproperty -path $Key
	       $b = $a.$Name
	       Return $b
       }
       else
       {
	       Return $null
       }
    }

function SetRegValue([string]$Key, [string]$Name, [string]$Value, [string]$Type)
    {
    # IF REGISTRY KEY EXISTS, THEN SET VALUES, OTHERWISE CREATE IT AND SET THE VALUES
       if(Test-Path $Key)
       {
       Set-ItemProperty -path $Key -name $Name -value $Value -Type $Type
       Return $true
       }
       else
       {
       New-Item -Path $Key
       Set-ItemProperty -path $Key -name $Name -value $Value -Type $Type
       Return $true
       }
    }

function ReadAndReportLastExecutionStatus
    {
      $Message = "Reading last execution details...";  LogMessage $Message
      if(GetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionDate -ne $null)
      {
		    $Message = "***"; LogMessage $Message
		    $Message = "***"; LogMessage $Message
		    $Message = "***"; LogMessage $Message
		    $a = GetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionDate
		    $Message = "Last Execution Date:              $($a)";  LogMessage $Message	   
		    $b = GetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionCommandLine
		    $Message = "Last Execution Command Line:      $($b)";  LogMessage $Message	   
		    $Message = ""; LogMessage $Message		
		    $c = GetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep1
		    $Message = "Last Execution Step1 (Uninstall HF/Leave Farm): $($c)";  LogMessage $Message	   
		    $d = GetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep2 	   	   
		    $Message = "Last Execution Step2 (Uninstall Core):   $($d)";  LogMessage $Message	   
		    $e = GetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep3
		    $Message = "Last Execution Step3 (Clean up / Prepare for Install): $($e)";  LogMessage $Message	   
		    $f = GetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep4
		    $Message = "Last Execution Step4 (Install / Upgrade):  $($f)";  LogMessage $Message	   	   
		    $g = GetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep5
		    $Message = "Last Execution Step5 (Clean up):  $($g)";  LogMessage $Message	   	   
		    $Message = "***"; LogMessage $Message
		    $Message = "***"; LogMessage $Message
		    $Message = "***"; LogMessage $Message
      }
      else
      {
	        $Message = "Script not executed previously on this machine, continuing...";  LogMessage $Message  
      }
     }

function ClearLastExecutionStatus
    {
      $Message = "Clearing Last Execution Status...";  LogMessage $Message
	       $a = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionDate $null "STRING"
	       $b = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionCommandLine $null "STRING"
	       $c = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep1 "NOT EXECUTED" "STRING"	   
	       $d = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep2 "NOT EXECUTED" "STRING"	   	   
	       $e = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep3 "NOT EXECUTED" "STRING"	   	   
	       $f = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep4 "NOT EXECUTED" "STRING"
	       $g = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep5 "NOT EXECUTED" "STRING"
	       return $true
     }

function SetExecutionStatus([int] $step, [String] $PassFail)
    {
     # IF SCRIPT JUST STARTED UP, THEN RESET THE EXECUTION DATE AND COMMAND LINE
     switch ($step)
     {
        0{
		       [string] $TimeStamp = Get-Date
               $Message = "Setting Execution Date and Command Line Values";  LogMessage $Message
		       $a = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionDate $TimeStamp "STRING"
	           $b = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionCommandLine $global:CommandLine "STRING"		
		       return $true
		       break;
        }
	
	    1{
               $Message = "Setting Execution Step 1 Pass/Fail result, Result: $($PassFail)";  LogMessage $Message	
	           $c = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep1 $PassFail "STRING"
		       return $true
		       break;
	    }
	
	    2{
               $Message = "Setting Execution Step 2 Pass/Fail result, Result: $($PassFail)";  LogMessage $Message		
	           $d = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep2 $PassFail "STRING"
		       return $true
		       break;
	    }

	    3{
               $Message = "Setting Execution Step 3 Pass/Fail result, Result: $($PassFail)";  LogMessage $Message		
	           $e = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep3 $PassFail "STRING"
		       return $true
		       break;
	    }

	    4{
               $Message = "Setting Execution Step 4 Pass/Fail result, Result: $($PassFail)";  LogMessage $Message		
	           $f = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep4 $PassFail "STRING"
		       return $true
		       break;
	    }
	    5{
               $Message = "Setting Execution Step 5 Pass/Fail result, Result: $($PassFail)";  LogMessage $Message		
	           $g = SetRegValue $global:ScriptStateInfoKey $global:ScriptStateLastExecutionStep5 $PassFail "STRING"
		       return $true
		       break;
	    }
        default{
               $Message = "Warning, attempting to set undefined Execution Status, Step: $($Step), Pass/Fail: $($PassFail), Continuing...";  LogMessage $Message			
		       return $false
	    }
     }
    }

function Verify-Snapins
{
$LoadedSnapins = Get-PSSnapin -Name *citrix* -ErrorAction SilentlyContinue | Select-Object Name
If($LoadedSnapins -ne $null)
    {
   # Write-Host "Successfully verified that necessary Citrix components are loaded in PowerShell" -ForegroundColor Yellow
    $Message = "Successfully verified that necessary Citrix components are loaded in PowerShell"; LogMessage $Message
    }
Else
    {
    $RegisteredSnapins = Get-PSSnapin -Name *citrix* -Registered | Select-Object Name
    $Found = $false
    foreach ($RS in $RegisteredSnapins) 
        {
        If($RS.Name -eq 'Citrix.XenApp.Commands')
            {
                $Found = $true
            }
        }
    If($Found) 
        {
        #Write-Host "Citrix components are not currently loaded in to PowerShell. Attempting to load components." -ForegroundColor DarkYellow
        $Message = "Citrix components are not currently loaded in to PowerShell. Attempting to load components."; LogMessage $Message
        Add-PSSnapin *citrix*
        Verify-Snapins
        }
    Else
        {
        $Message = "The necessary Citrix components to execute this script are not installed on this computer."; LogMessage $Message
        Read-Host "The necessary Citrix components to execute this script are not installed on this computer. Press enter to exit."
        Exit
        }
    }
}

function Uninstall_CitrixHotFix($hfs) {
    foreach ($hf in $hfs) {

        $ThisFilePath = "C:\Program Files (x86)\Common Files\Citrix\System32\cpatch.exe"
        $ThisArgumentList = "/s /i " + $hf.HotfixName

        #$return = (Start-Process "C:\Program Files (x86)\Common Files\Citrix\System32\cpatch.exe" /s /i $hf.HotfixName -PassThru)
        $return = (Start-Process -FilePath $ThisFilePath -argumentlist $ThisArgumentList -PassThru)

        $continue = $true

        while($return.HasExited -eq $false) {
            If ((New-TimeSpan -Start $return.StartTime).Seconds -gt 3) 
            {
                $return.Kill()
               # Stop-Process $return -Force
                $continue = $false
            }
            else
            {
                Sleep -Seconds 1
            }
        }

        if($continue) {
            $ProductCode = & "C:\Program Files (x86)\Common Files\Citrix\System32\cpatch.exe" /s /i $hf.HotfixName | findstr ProductCode | out-string
            $PatchCode = & "C:\Program Files (x86)\Common Files\Citrix\System32\cpatch.exe" /s /i $hf.HotfixName | findstr PatchCode | out-string

            $ProductCode = $ProductCode.replace("ProductCode= ","")
            $PatchCode = $PatchCode.replace("PatchCode = ","")

            $ProductCode = $ProductCode.replace("`r`n","")
            $PatchCode = $PatchCode.replace("`r`n","")

            $a = UninstallHotFix $ProductCode $PatchCode $hf.HotfixName
            if($a -eq 1603) {
                $Message = ""; LogMessage $Message
                $Message = "Failed to uninstall HotFixes Automatically - Remove Manually and try again..."; LogMessage $Message
                $Message = ""; LogMessage $Message
                $a = SetExecutionStatus -step 1 -PassFail "FAIL"
                TaskCleanup;
                Exit
            }
        } 
    }
}

function RemoveComputerFromGroup($ComputerName,$GroupName)
{
    
    $Result = @()
    $Group = Get-ADGroup  -Filter * | where {$_.name -eq $GroupName}
    $Computer=  Get-ADComputer  -filter *  | where { $_.Name -eq $ComputerName}
	#Check if the specified group exists
    If($Group)
    {
        $GroupDN = $Group.DistinguishedName
        $GroupObj = [ADSI]"LDAP://$GroupDN"
        #Check if the computer exists
        If($Computer)
        {
			$ComputerDN =  $Computer.DistinguishedName
			$ComputerLDAP  = "LDAP://$ComputerDN" 
			#Check if the computer is a member of the group
            if($GroupObj.IsMember($ComputerLDAP))
            {
                Try
                {
					#Remove computer
                    $GroupObj.Remove($ComputerLDAP) 
                    $Result += New-Object PSObject -Property @{ComputerName = $ComputerName; Result = "Removed" }

                }
                Catch 
                {
                    $Result += New-Object PSObject -Property @{ComputerName = $ComputerName; Result = "Failed" }
                }

            }
            Else
            {
                 $Result += New-Object PSObject -Property @{ComputerName = $ComputerName; Result = "NotFound" }
            }
        }
        Else
        {
            $Result += New-Object PSObject -Property @{ComputerName = $ComputerName; Result = "NotFound" }
        }

        $Result 
    }
    Else
    {
        Write-Error "There is no group named $GroupName."
    }

}

function UpgradeType
{
    $ctxver = GetRegValue "HKLM:\SOFTWARE\Citrix\Versions\Citrix Virtual Desktop Agent" "Citrix Virtual Desktop Agent - x64"
    If($ctxver -eq $null) {
        $ctxver = GetRegValue "HKLM:\SYSTEM\CurrentControlSet\Control\Citrix" "ProductVersion"
    }

    If($ctxver -eq $null) {
        return $null
    }
    $version = $ctxver.Split(".")
    [int]$mainver = $version[0]
    [int]$minorver = $version[1]
    [int]$subver = $version[2]

    if($mainver -eq 7 -AND $minorver -eq 15 -AND $subver -eq 3000) {
#        if($minorver -eq 15) {
#        if($minorver -lt 15) {
            return $subver
        } Else {
            return $mainver
#        }
    }

    if($mainver -eq 6) {
        return $mainver
    }

    return $null
}

#***************************************************************
#***************************************************************
#***** MAIN CODE - BEGIN PROCESSING                       ******
#***************************************************************
#***************************************************************

If (!(Test-Path "C:\Company")) {
     $return = New-Item "C:\Company" -type directory
}

$Message = ""; LogMessage $Message
$Message = ""; LogMessage $Message
$Message = ""; LogMessage $Message
$Message = "Begin Processing..."; LogMessage $Message

$Message = ""; LogMessageQuiet $Message
$Message = "Put the server in Downtime in Check_MK for 2 hours..."; LogMessageQuiet $Message
Write-Host
Write-Host
Write-Host "Put the server in Downtime in Check_MK for 2 hours..." -ForegroundColor Red 



switch ([System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite().Name) 
{
    "First-Site"
    {
        $Message = ""; LogMessage $Message
        $Message = ""; LogMessage $Message
        $Message = "Running with Portland Settings..."; LogMessage $Message
        $global:UpgradeFilesPath = "\\CONTOSO.com\PATH\Citrix\XenApp-Upgrade"
        $global:DeliveryControllers = "SERVERNAME.CONTOSO.COM,SERVERNAME.CONTOSO.COM"
        $global:AdminAddress = 'SERVERNAME.CONTOSO.COM:80'
        $global:StudioServer = 'SERVERNAME.CONTOSO.COM'
        break
    }

    "Another-Site"
    {
        $Message = ""; LogMessage $Message
        $Message = ""; LogMessage $Message
        $Message = "Running with Atlanta Settings..."; LogMessage $Message
        $global:UpgradeFilesPath = "\\CONTOSO.com\PATH\Citrix\XenApp-Upgrade"
        $global:DeliveryControllers = "SERVERNAME.CONTOSO.COM,SERVERNAME.CONTOSO.COM"
        $global:AdminAddress = 'SERVERNAME.CONTOSO.COM:80'
        $global:StudioServer = 'SERVERNAME.CONTOSO.COM'
        break
    }
    default
    {
        $Message = ""; LogMessage $Message
        $Message = ""; LogMessage $Message
        $Message = "Script does not appear to be in a known Site...Exiting!"; LogMessage $Message
        Exit
    }

}

#***************************************************************
#***************************************************************
# TASK SCHEDULING TO CONTINUE SCRIPT AFTER REBOOTS
#***************************************************************
#***************************************************************

if ($NextStep -ge 0) {
        $members = net localgroup administrators | 
            where {$_ -AND $_ -notmatch "command completed successfully"} | 
            select -skip 4

        if(!($members -contains "CONTOSO\anAdminsGroup")) {
            $Message = ""; LogMessage $Message
            $Message = ""; LogMessage $Message
            $Message = "anAdminsGroup is not a member of the local Administrators group. Attempting to add."; LogMessage $Message
            $Message = net localgroup administrators CONTOSO\anAdminsGroup /add
            LogMessage $Message
            $members = net localgroup administrators | 
                where {$_ -AND $_ -notmatch "command completed successfully"} | 
                select -skip 4
            if(!($members -contains "CONTOSO\anAdminsGroup")) {

                $Message = ""; LogMessage $Message
                $Message = ""; LogMessage $Message
                $Message = "anAdminsGroup is not a member of the local Administrators group. Can not continue...EXITING"; LogMessage $Message
                EXIT 1
            }
        }

        #Verify the upgrade should be completed and what steps to run.
        if($NextStep -eq 0) {
        #Create a SnapShot of the VM Before we do ANYTHING else. 
        $CheckPointComputerName = $env:COMPUTERNAME

        if ((Test-Path -Path $global:CPPath$CheckPointComputerName) -or (Test-Path -Path $global:CPPath$CheckPointComputerName-complete) ) {
            #CheckPoint files already exist. There is something wrong. Abort and Troubleshoot before restarting.
            $MessageText = "Checkpoint file exists in: " + $global:CPPath + "  Error Deploying Citrix Xenapp on server: " +  $CheckPointComputerName
            $MessageSubj = "Error Deploying Citrix Xenapp on XenServer: " + $CheckPointComputerName
            $XenAppTo = "person@contoso.com"

            Send-MailMessage -Body $MessageText -From $global:FromAddress -To $XenAppTo -Subject $MessageSubj -SmtpServer $global:SMTPServer -Credential $global:Cred
            Exit
        } else {
            $CheckPointComputerName | Out-File -FilePath $global:CPPath$CheckPointComputerName -Append
            $CheckPointTimer = 0
            Do {
                Sleep -Seconds 10
                $CheckPointTimer++
            }
            Until ((Test-Path -Path $global:CPPath$CheckPointComputerName-complete) -or ($CheckPointTimer -gt 60))
            If ($CheckPointTimer -gt 60) {
                #Problem creating checkpoint. Aborting.
                $MessageText = "Problem creating Checkpoint for server: " +  $CheckPointComputerName
                $MessageSubj = "Problem creating Checkpoint on XenServer: " + $CheckPointComputerName
                $XenAppTo = "person@contoso.com"

                Send-MailMessage -Body $MessageText -From $global:FromAddress -To $XenAppTo -Subject $MessageSubj -SmtpServer $global:SMTPServer -Credential $global:Cred
                Exit
            }
            Remove-Item -Path $global:CPPath$CheckPointComputerName-complete
        }


        SetRegValue $global:ScriptStateInfoKey $global:ScriptStateNewInstallName 0 "DWORD"
            switch (UpgradeType) {

                6{
                    $Message = ""; LogMessage $Message
                    $Message = ""; LogMessage $Message
                    $Message = "Found Citrix Version 6.x. Performing full upgrade."; LogMessage $Message
                    break
                }

                7{
                    $Message = ""; LogMessage $Message
                    $Message = ""; LogMessage $Message
                    $Message = "Found Citrix Version 7.x and less than 7.15.3000 Performing upgrade to version 7.15.3000"; LogMessage $Message
                    $global:seven = $true
                    $NextStep = 3
                    SetRegValue $global:ScriptStateInfoKey $global:ScriptStateNewInstallName 1 "DWORD"
                    break
                }

                3000{
                    $Message = ""; LogMessage $Message
                    $Message = ""; LogMessage $Message
                    $Message = "Found Citrix Version 7.15.3000 already installed. Not performing any actions. Exiting..."; LogMessage $Message
                    Exit
                }

                default{
                    $Message = ""; LogMessage $Message
                    $Message = ""; LogMessage $Message
                   # $NewInstall = Read-Host "Is this a New Install? (y/n)"
                   # If(($NewInstall.ToLower() -eq "y") -or ($NewInstall.ToLower() -eq "yes")) {
                        $global:seven = $true
                        $NextStep = 3
                        $global:FreshInstall = $true
                        SetRegValue $global:ScriptStateInfoKey $global:ScriptStateNewInstallName 1 "DWORD"
                        $Message = "Installing New 7.15 server..."; LogMessage $Message
                   # } Else {
                   #     $Message = "Unknown error. Not performing any actions. Exiting..."; LogMessage $Message
                   #     Exit
                   # }
                    break                   
                }
            }        
        }

        # LOG TASK SCHEDULED DETAILS
        $Message = ""; LogMessage $Message
		$Message = "Scheduling task, using task name: $($global:taskname)"; LogMessage $Message

        $Message = ""; LogMessage $Message
		$Message = "Using the following Log Folder: $($global:LogFolder)"; LogMessage $Message
		$Message = "Using the following Script Log: $($global:LogFile)"; LogMessage $Message		

        # DISPLAY PREVIOUS EXECUTION DETAILS

        ReadAndReportLastExecutionStatus;

        # INFORM THE USER THAT THEY MUST LOGOFF ALL USER SESSIONS AND CLOSE ALL WINDOWS BEFORE PROCEEDING
		$Message = ""; LogMessage $Message
		$Message = ""; LogMessage $Message
		$Message = ""; LogMessage $Message
		
        $Message = "IMPORTANT - Before continuing, please ensure that all users are logged off this server and that all windows are closed in this session."; LogMessage $Message
		
		$Message = ""; LogMessage $Message
		$Message = ""; LogMessage $Message
		$Message = ""; LogMessage $Message

        # DELETE THE SCHEDULED TASK IF SOMEHOW IT IS ALREADY RUNNING
        if(IsTaskScheduled($taskname))
        	{
		    	SCHTASKS /DELETE /TN "$($taskname)" /F
            }

		SCHTASKS /create /RL HIGHEST /tn "$($taskname)" /tr "powershell -ExecutionPolicy Bypass -c $($myinvocation.mycommand.definition) -NextStep -1 >> $($global:LogFolder)\OUTPUTSTREAM.LOG" /sc onstart /ru "$($global:TaskUserName)" /rp "$($global:TaskUserPW)" 

        # VERIFY THAT THE TASK IS SCHEDULED PROPERLY.  IF NOT LOG AN ERROR AND STOP
        if(!(IsTaskScheduled($taskname)))
        	{
		        $Message = "Failed to schedule the task.  Halting...";
            	Exit
            }

        # CLEAR LAST EXECUTION STATUS

        $a = ClearLastExecutionStatus;
		
        # SET CURRENT EXECUTION DATE AND COMMAND LINE		

        $a = SetExecutionStatus -step 0 -PassFail "PASS"

		if ($NextStep -eq 0) {$myvalue = 1}
		else {$myvalue = $NextStep}
        
        SetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName $myvalue "DWORD"

        $Message = "Modifying Script Step to: $($myvalue)"; LogMessage $Message

}


#***************************************************************
#***************************************************************
# STANDARD EXECUTION WITHOUT TASK SCHEDULING BEGINS HERE
#***************************************************************
#***************************************************************
$step = GetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName
$Message = "Processing script step: $($step)"; LogMessage $Message

switch ($step)
{
    1{
        $Message = ""; LogMessage $Message
        $Message = "Verifying Snapins..."; LogMessage $Message

        Verify-Snapins
        # **************** UNINSTALL HOTFIXES, LEAVE FARM, UNINSTALL LEGACY XENAPP ***************

        $hfs = Get-XAServerHotfix $env:computername | where {$_.HotfixType -eq "HRP" } | sort-object HotfixType,Installedon -desc
        if($hfs -ne $null) {
            Uninstall_CitrixHotFix $hfs
        }

        $hfs = Get-XAServerHotfix $env:computername | sort-object HotfixType,Installedon -desc
        if($hfs -ne $null) {
            Uninstall_CitrixHotFix $hfs
        }

        $Message = "Removing Citrix Snapins..."; LogMessage $Message
        Remove-PSSnapin citrix*

		$Message = "LEAVING Current Farm..."; LogMessage $Message 

		$ThisFilePath = "c:\Program Files (x86)\Citrix\XenApp\ServerConfig\xenappconfigconsole.exe"
		$ThisArgumentList = "/Executionmode:Leave /Log:$($global:logfolder)\LeaveXenAppFarmLog.txt"		
		
		$return = (start-process -FilePath $ThisFilePath -argumentlist $ThisArgumentList -wait -passthru).ExitCode
		
        if ($return -eq 0)
		{
		$Message = "SUCCESS - Return Value: $($return)"; LogMessage $Message
		}
		else
		{
		$Message = "Warning - Leave farm Failed with Return Value: $($return).  Continuing..."; LogMessage $Message		
		}

        $myvalue = $step + 1
        SetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName $myvalue "DWORD"

        $a = SetExecutionStatus -step 1 -PassFail "PASS"
		# LOG REBOOT
		$TimeStamp = get-date
		$Message = "Rebooting machine in 15 seconds..."; LogMessage $Message

		Start-Sleep -Seconds 15

        Restart-Computer -force

        break;
    }

    2{
        $myvalue = $step + 1
        SetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName $myvalue "DWORD"

        #$Message = "Sleeping for 120 seconds waiting for services to initialize"; LogMessage $Message
       # Start-Sleep -Seconds 120

		
		
		# Citrix XenApp 6.0
		$ThisInformalName = "Citrix XenApp 6.0"
		$ThisGUID = "{A85634C1-370D-4A35-A872-8337942A570B}"
		$return = UninstallPackage $ThisGUID $ThisInformalName
		if(($return -ne 0) -and ($return -ne 3010))
		{
			$Message = "FAILURE attempting to Uninstall Legacy XenApp, Halting..."; LogMessage $Message
			$a = SetExecutionStatus -step 1 -PassFail "FAIL"
            TaskCleanup
			Exit
		}
		
		#Citrix HDX MediaStream for Flash - Server
		$ThisInformalName = "Citrix HDX MediaStream for Flash - Server"
		$ThisGUID = "{3F7B1F37-4450-4647-8E7B-EF7AFE05C00C}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		#Citrix Common Commands
		$ThisInformalName = "Citrix Common Commands"
		$ThisGUID = "{23640E03-4296-470E-B4C2-667E0091F8B0}"
        $ThisTaskName = "Citrix_Common_Commands"
        $ThisDelay = "0000:02"
		$a = UninstallPackage $ThisGUID $ThisInformalName
#        $a = ScheduleUninstallPackage $ThisGUID $ThisInformalName $ThisTaskName $ThisDelay

		#Citrix XenApp Commands
		$ThisInformalName = "Citrix XenApp Commands"
		$ThisGUID = "{AAE18511-1041-40A5-9A50-E7C07DD6A504}"
        $ThisTaskName = "Citrix_XenApp_Commands"
        $ThisDelay = "0000:32"
		$a = UninstallPackage $ThisGUID $ThisInformalName
#        $a = ScheduleUninstallPackage $ThisGUID $ThisInformalName $ThisTaskName $ThisDelay

		#Citrix Group Policy Client-Side Extension (x64)
		$ThisInformalName = "Citrix Group Policy Client-Side Extension"
		$ThisGUID = "{048AD881-32B3-4B6D-AA1F-87FDC3F061BB}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		#Citrix XenApp Server Configuration Tool
		$ThisInformalName = "Citrix XenApp Server Configuration Tool"
		$ThisGUID = "{87819181-D0F3-4CAE-8144-D614FCC8E41E}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		#Citrix XenApp Server Role Manager
		$ThisInformalName = "Citrix XenApp Server Role Manager"
		$ThisGUID = "{EF29242B-2B9F-4437-B69A-03C0984FB506}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		#Citrix XenApp Management
		$ThisInformalName = "Citrix XenApp Management"
		$ThisGUID = "{CA427D8B-7DB7-4051-ADA5-7775C6330CBB}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		# Citrix Single Sign-On Console 4.8
		$ThisInformalName = "Citrix Single Sign-On Console 4.8"
		$ThisGUID = "{577D005B-1E30-457C-BF1A-E1A82AF53B5C}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		#Citrix XenApp Group Policy Management Experience (x64)
		$ThisInformalName = "Citrix XenApp Group Policy Management Experience"
		$ThisGUID = "{92978410-2ACD-4F91-87FB-A7BE0EF2D19E}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		#Citrix Single Sign-On Plug-in
		$ThisInformalName = "Citrix Single Sign-On Plug-in"
		$ThisGUID = "{CFC65316-3DE7-461E-940D-873FB386E742}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		#Citrix Profile Management
		$ThisInformalName = "Citrix Profile Management"
		$ThisGUID = "{17465CC9-06A6-4CCF-B0DB-C9D097982467}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

		# Citrix offline plug-in  
		$ThisInformalName = "Citrix offline plug-in"
		$ThisGUID = "{5259E748-0F5B-4938-98A1-FE430D8B869B}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

<# Citrix Online Plug-in has never successfully uninstalled using this script - Rather than wait, just uninstall manually at the end...

		# Citrix Online Plug-in 
        Start-Sleep -Seconds 60
		$Message = "Uninstalling Citrix online plug-in..."; LogMessage $Message
		$ThisFilePath = "C:\ProgramData\Citrix\Citrix online plug-in\TrolleyExpress.exe"
		$ThisArgumentList = "/silent /uninstall /cleanup"

		if(!(Test-Path -Path $ThisFilePath -pathtype Leaf ))
		{
			$Message = "FAILURE attempting to locate Citrix Online Plugin executable at $($ThisFilePath), continuing..."; LogMessage $Message
		}
		else
        {
		    $return = (start-process -FilePath $ThisFilePath -argumentlist $ThisArgumentList -passthru)

            while($return.HasExited -eq $false) {
                If ((New-TimeSpan -Start $return.StartTime).Minutes -gt 2) 
                {
                    $return.Kill()
                    #Stop-Process $return -Force
                }
                else
                {
                    Sleep -Seconds 3
                }
            }

		
		    if ($return.ExitCode -eq 0)
		    {
			    $Message = "SUCCESS - Return Value: $($return)"; LogMessage $Message
            }
		    else
		    {
			    $Message = "WARNING - Citrix online plug-in failed to uninstall with Return Value: $($return.ExitCode).  Continuing..."; LogMessage $Message		
		    }
        }

#>  #End of Citrix On-Line Plugin UnInstall Routine

<# Leave these prereqs installed.		

        # Microsoft Visual C++ Redistributables
        $Products = Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -like "*Visual C++*"}
        ForEach($Product in $Products)
            {
            $myObj = "" | Select-Object ThisInformalName,ThisGUID
            $myObj.ThisInformalName = $Product.Name
            $myObj.ThisGUID = $Product.IdentifyingNumber
            UninstallPackage2 $myObj.ThisGUID $myObj.ThisInformalName
            }

        # Microsoft Primary Interoperability Assemblies 2005
		$ThisInformalName = "Microsoft Primary Interoperability Assemblies 2005"
		$ThisGUID = "{2C303EE0-A595-3543-A71A-931C7AC40EDE}"
		$a = UninstallPackage $ThisGUID $ThisInformalName

#>

        $a = SetExecutionStatus -step 2 -PassFail "PASS"
		# LOG REBOOT
		$TimeStamp = get-date
		$Message = "Rebooting machine in 15 seconds..."; LogMessage $Message

		Start-Sleep -Seconds 15

        Restart-Computer -force

        break;
    }

    3{

    #***************************************************************
    #***************************************************************
    # CLEANUP BEFORE XENAPP 7.15 INSTALLATION
    #***************************************************************
    #***************************************************************
    $myvalue = $step + 1
    SetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName $myvalue "DWORD"


    
    If(($global:seven) -and !($global:FreshInstall)) {
        $Message = "Putting server in maintenance mode..."; LogMessage $Message

        $MachineName = "Contoso.com\" + $env:computername
        #Invoke-Command -ComputerName $global:StudioServer -ScriptBlock { asnp citrix*; Get-BrokerMachine -AdminAddress $global:AdminAddress -MachineName $MachineName | Set-BrokerMachineMaintenanceMode -AdminAddress $global:AdminAddress -MaintenanceMode $True }
        Invoke-Command -ComputerName $global:StudioServer -Credential $global:Cred -ScriptBlock { param($RAA, $RMN) asnp citrix*; Get-BrokerMachine -AdminAddress $RAA -MachineName $RMN | Set-BrokerMachineMaintenanceMode -AdminAddress $RAA -MaintenanceMode $True } -ArgumentList ($global:AdminAddress, $MachineName)  

        #Notify any connected users of the impending restart.
        Invoke-Command -ComputerName $global:StudioServer -Credential $global:Cred -ScriptBlock { param($RAA, $RMN) asnp citrix*; Get-BrokerSession -AdminAddress $RAA -MachineName $RMN | Send-BrokerSessionMessage -AdminAddress $RAA -MessageStyle Information -Title "Maintenance Message from COMPANY" -Text "The server you are connected to will be restarted for maintenance shortly. Please save any work and log out now." } -ArgumentList ($global:AdminAddress, $MachineName)  
    }  #End upgrade of 7.xx commands

#Skip performing disk cleanup as it takes too long and often causes the script to fail.
<#    
        $Message = "Creating registry values for disk cleanup"; LogMessage $Message

        $ExplorerVolumeCaches = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches"
        $CleanUp = @()
        $CleanUp += "$ExplorerVolumeCaches\Active Setup Temp Folders"
        $CleanUp += "$ExplorerVolumeCaches\Downloaded Program Files"
        $CleanUp += "$ExplorerVolumeCaches\Internet Cache Files"
        $CleanUp += "$ExplorerVolumeCaches\Memory Dump Files"
        $CleanUp += "$ExplorerVolumeCaches\Offline Pages Files"
        $CleanUp += "$ExplorerVolumeCaches\Old ChkDsk Files"
        $CleanUp += "$ExplorerVolumeCaches\Previous Installations"
        $CleanUp += "$ExplorerVolumeCaches\Recycle Bin"
        $CleanUp += "$ExplorerVolumeCaches\Service Pack Cleanup"
        $CleanUp += "$ExplorerVolumeCaches\Setup Log Files"
        $CleanUp += "$ExplorerVolumeCaches\System error memory dump files"
        $CleanUp += "$ExplorerVolumeCaches\System error minidump files"
        $CleanUp += "$ExplorerVolumeCaches\Temporary Files"
        $CleanUp += "$ExplorerVolumeCaches\Temporary Setup Files"
        $CleanUp += "$ExplorerVolumeCaches\Thumbnail Cache"
        $CleanUp += "$ExplorerVolumeCaches\Update Cleanup"
        $CleanUp += "$ExplorerVolumeCaches\Upgrade Discarded Files"
        $CleanUp += "$ExplorerVolumeCaches\Windows Error Reporting Archive Files"
        $CleanUp += "$ExplorerVolumeCaches\Windows Error Reporting Queue Files"
        $CleanUp += "$ExplorerVolumeCaches\Windows Error Reporting System Archive Files"
        $CleanUp += "$ExplorerVolumeCaches\Windows Error Reporting System Queue Files"
        $CleanUp += "$ExplorerVolumeCaches\Windows Upgrade Log Files"

        # Define variables for the set of options
        $Sageset = "0010"
        $StateFlags = "StateFlags$SageSet"
        $StateFlagsValue = "2"

        # Loop through each of the above registry values and set up cleanup
        ForEach($Clean in $CleanUp)
        {
            If((Test-Path -Path $Clean) -eq $true)
            {
                Set-ItemProperty -Path $Clean -Name $StateFlags -Value $StateFlagsValue -Type DWord
            }
        }

        # Run cleanmgr.exe
        $Message = "Running Disk Cleanup"; LogMessage $Message
    #    & "C:\Windows\System32\Cleanmgr.exe" + " /sagerun:$Sageset"
    #    Wait-Process Cleanmgr

    #
    # Using Start-Process instead of "&" because the Cleanmgr.exe process wasn't exiting properly upon completion using "&". 
    # Reason unknown...others on the Internet reported success using Start-Process and testing was successful here.
    #
        Start-Process cleanmgr `
                -ArgumentList "/sagerun:$Sageset" `
                -Wait `
                -NoNewWindow `
                -ErrorAction   SilentlyContinue `
                -WarningAction SilentlyContinue
#>
#End skipping disk cleanup

    # Fix RDP-Tcp Listener
    # Skip this step if upgrading version 7.x to 7.15 or new 7.15 installation
    If(!($global:seven)) {
        $Message = "Fixing RDP Listener Configuration, cleaning up XenApp 6.0 component"; LogMessage $Message
        $RDPReg = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
        $RDPRegName = "LoadableProtocol_Object"
        $ICARegName = "CitrixBackupRdpTcpLoadableProtocolObject"
        $RDPRegValue = “{18b726bb-6fe6-4fb9-9276-ed57ce7c7cb2}”
        SetRegValue $RDPReg $RDPRegName $RDPRegValue "STRING"
        Remove-ItemProperty -Path $RDPReg -Name $ICARegName

    }
    
       

    # Reboot
    $a = SetExecutionStatus -step 3 -PassFail "PASS"
    $TimeStamp = get-date
    $Message = "Rebooting machine in 15 seconds..."; LogMessage $Message

    Start-Sleep -Seconds 15

    Restart-Computer -force

    break;
    }

    4{
    #***************************************************************
    #***************************************************************
    # XENAPP 7.15 INSTALLATION BEGINS HERE
    #***************************************************************
    #***************************************************************
    $myvalue = $step + 1
    SetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName $myvalue "DWORD"


    # Reset Permissions on Citrix EUEM registry
    If(Test-Path -Path HKLM:\software\WOW6432Node\Citrix\EUEM\LoggedEvents) {
    #If(!($global:FreshInstall)) {
        $Message = "Fixing Citrix EUEM registry permissions..."; LogMessage $Message
        Take-Permissions "HKLM" "SOFTWARE\WOW6432Node\Citrix\EUEM\LoggedEvents" "S-1-5-32-544"
        $acl = get-acl -Path "HKLM:\software\WOW6432Node\Citrix\EUEM\LoggedEvents"
        $inheritance = [System.Security.AccessControl.InheritanceFlags]"ObjectInherit,ContainerInherit"
        $propagation = [System.Security.AccessControl.PropagationFlags]"None"
        $rule = New-Object System.Security.AccessControl.RegistryAccessRule ("LOCAL SERVICE","FullControl",$inheritance,$propagation,"Allow")
        $acl.SetAccessRule($rule)
        $acl | set-acl
    }

    #Copy Files
    $Message = "Copying XenApp 7.15 files"; LogMessage $Message
    mkdir $global:UpgradeFiles
    ROBOCOPY $global:UpgradeFilesPath $global:UpgradeFiles *.* 

    Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Installer" -Name "RemappedElevatedProxiesPolicy" -Value 1 -Type Dword

    $Installer = "C:\Company\XenApp-Upgrade\VDAServerSetup_7_15_3000.exe"
    $Options = '/controllers "' + $global:DeliveryControllers + '" /noreboot /quiet /enable_remote_assistance /disableexperiencemetrics /components VDA /virtualmachine /optimize /enable_real_time_transport /enable_hdx_ports /enable_hdx_udp_ports /exclude "Smart Tools agent","Citrix Telemetry Service","Citrix Personalization for App-V - VDA","Machine Identity Service","Personal vDisk"'
    if(!(Test-Path -Path $Installer -PathType Leaf))
    {
        Copy-Item -Path "$global:UpgradeFilesPath\*.*" -Destination $global:UpgradeFiles -Force

        if(!(Test-Path -Path $Installer -PathType Leaf))
        {

            $Message = "FAILURE attempting to locate XenApp 7.15 files. Halting..."; LogMessage $Message
            $a = SetExecutionStatus -step 4 -PassFail "FAIL"
            Start-Service -Name AMPWatchDog
            Start-Service -Name konea
            Start-Service -Name OfflineScheduler

            TaskCleanup
            return $false
        }
    }

    #Run Installer
    $Message = "Adding .exe;.msp;.msi files to white-list for prompts"; LogMessage $Message
    Add-LowRiskFiles
    $Message = "Starting XenApp 7.15 installation"; LogMessage $Message

    $sb_KillSpoolSvAfter5Minutes = {  
        Sleep -Seconds 360
        $spool = get-process | where {$_.ProcessName -eq "spoolsv"}
        $spool.Kill()

    }

    $sb_KillSpoolSvWhenMoreThanOne = {
        while($true)
        {  
            Start-Sleep -Seconds 5
            $spool = get-process | where {$_.ProcessName -eq "spoolsv"}
            if($spool.Count -gt 1) 
            {
                $spool | Stop-Process -Force
            }
        }
    }
    Start-Job -ScriptBlock $sb_KillSpoolSvWhenMoreThanOne

 #   Start-Job -ScriptBlock $sb_KillSpoolSvAfter5Minutes

    #$return = (Start-Process $Installer -ArgumentList $Options -PassThru)
    $return = (Start-Process $Installer -ArgumentList $Options -Wait -NoNewWindow -PassThru).ExitCode
<#
    $NotKilled = $true

    while($return.HasExited -eq $false) {
        If ( ((New-TimeSpan -Start $return.StartTime).Minutes -gt 5) -and ($NotKilled) )
        {
            $spool = get-process | where {$_.ProcessName -eq "spoolsv"}
            $spool.Kill()
            $NotKilled = $false
        }
        else
        {
            Sleep -Seconds 10
        }
    }
#>

#	$Message = $return; LogMessage $Message
#   $return | Out-File "C:\Contoso\TestFile.txt"
#   $return
#   $return | gm

	
#    $Message = "Finished Installing XenApp 7.15, return value: $($return.ExitCode)"; LogMessage $Message
  
#    if (($return.ExitCode -eq $null) -or ($return.ExitCode -eq 0) -or ($return.ExitCode -eq 3) -or ($return.ExitCode -eq 3010))
    $Message = "Finished Installing XenApp 7.15, return value: $($return)"; LogMessage $Message
  
    if (($return -eq 0) -or ($return -eq 3) -or ($return -eq 3010))
		{
            if(GetRegValue $global:ScriptStateInfoKey $global:ScriptStateNewInstallName) {

                SetRegValue $global:ScriptStateInfoKey $global:BackupDUN $(GetRegValue $global:ScriptWinlogon $global:ScriptDefaultUserName) "STRING"
                SetRegValue $global:ScriptStateInfoKey $global:BackupPW $(GetRegValue $global:ScriptWinlogon $global:ScriptDefaultPassword) "STRING"
                SetRegValue $global:ScriptStateInfoKey $global:BackupAAL $(GetRegValue $global:ScriptWinlogon $global:ScriptAutoAdminLogon) "DWORD"

                SetRegValue $global:ScriptWinlogon $global:ScriptDefaultUserName $global:TaskUserName "STRING"
                SetRegValue $global:ScriptWinlogon $global:ScriptDefaultPassword $global:TaskUserPW "STRING"
                SetRegValue $global:ScriptWinlogon $global:ScriptAutoAdminLogon "1" "DWORD"
            }

            $a = SetExecutionStatus -step 4 -PassFail "PASS"		
		}
		else
		{
           $a = SetExecutionStatus -step 4 -PassFail "FAIL"		
		   $Message = "Failed to Install XenApp, Halting..."; LogMessage $Message

           $MessageText = "Failed to install XenApp on server: " +  $env:computername
           $MessageSubj = $env:computername + " failure."

           Send-MailMessage -Body $MessageText -From $global:FromAddress -To $global:ToAddress -Subject $MessageSubj -SmtpServer $global:SMTPServer -Credential $global:Cred
           Start-Service -Name AMPWatchDog
           Start-Service -Name konea
           Start-Service -Name OfflineScheduler
         
	       # DELETE SCHEDULED TASK HERE
		   TaskCleanup;
		   ;
		   
           return $false
		   
		}

    $Message = "Rebooting machine in 15 seconds..."; LogMessage $Message
    Start-Sleep -Seconds 15
    Restart-Computer -force
    }

    5{
    If(GetRegValue $global:ScriptStateInfoKey $global:ScriptStateNewInstallName) {
        $Message = "Sleeping for 60 seconds waiting for services to initialize"; LogMessage $Message
        Start-Sleep -Seconds 60

        $Message = "Waiting for installation to complete..."; LogMessage $Message
        SetRegValue $global:ScriptStateInfoKey $global:ScriptStateNewInstallName 0 "DWORD"

        SetRegValue $global:ScriptWinlogon $global:ScriptDefaultUserName $(GetRegValue $global:ScriptStateInfoKey $global:BackupDUN) "STRING"
        SetRegValue $global:ScriptWinlogon $global:ScriptDefaultPassword $(GetRegValue $global:ScriptStateInfoKey $global:BackupPW) "STRING"
        SetRegValue $global:ScriptWinlogon $global:ScriptAutoAdminLogon $(GetRegValue $global:ScriptStateInfoKey $global:BackupAAL) "DWORD"

        SetRegValue $global:ScriptStateInfoKey $global:BackupDUN $null "STRING"
        SetRegValue $global:ScriptStateInfoKey $global:BackupPW $null "STRING"
        SetRegValue $global:ScriptStateInfoKey $global:BackupAAL 0 "DWORD"


        while ( ( (get-process | where { $_.Name -like "*Setup*" }).Count ) -gt 0 ) {
            Start-Sleep -Seconds 15
        }

        $Message = "Rebooting machine in 15 seconds..."; LogMessage $Message
        Start-Sleep -Seconds 15
        Restart-Computer -force
        Exit
    }


    $myvalue = $step + 1
    SetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName $myvalue "DWORD"

    $Message = "Sleeping for 60 seconds waiting for services to initialize"; LogMessage $Message
    Start-Sleep -Seconds 60
<# SKIPPING PATCHES FOR 7.15 - these patches are for 7.6 only
    #Time for patches
    $Message = "Installing patches"; LogMessage $Message
    $PatchBase = "C:\Contoso\XenApp-Upgrade"
    $Patch1 = "$($PatchBase)\BrokerAgent760WX64001.msp"
    $Patch2 = "$($PatchBase)\ICATS760WX64006.msp"
    $Patch3 = "$($PatchBase)\ICATS760WX64008.msp"
    $Patch4 = "$($PatchBase)\ICATS760WX64009.msp"
    $Patch5 = "$($PatchBase)\ICATS760WX64010.msp"
    $Patch6 = "$($PatchBase)\ICATS760WX64012.msp"

    InstallPatch $Patch1
    InstallPatch $Patch2
    InstallPatch $Patch3
    InstallPatch $Patch4
    InstallPatch $Patch5
    InstallPatch $Patch6

    #UPM 5.2.1
    $Message = "Installing UPM 5.2.1"; LogMessage $Message
    $UPMInstaller = "$($PatchBase)\ProfileMgt520WX64001.msi"
    $UPMOptions = "/qn /norestart REBOOT=ReallySuppress"
    Start-Process $UPMInstaller -ArgumentList $UPMOptions -Wait
#>
    #Clean-up Schedeuled Task
    If ( (Get-Service "BrokerAgent" -ErrorAction SilentlyContinue) -eq $null) 
    {
        $myvalue = 4
        SetRegValue $global:ScriptStateInfoKey $global:ScriptStateInfoName $myvalue "DWORD"

        $Message = "Citrix didn't install fully. Trying again."; LogMessage $Message
        $Message = "Rebooting machine in 15 seconds..."; LogMessage $Message

        Start-Sleep -Seconds 15

        Restart-Computer -force

        break;

    }

    TaskCleanup;


    #Forcing GPUpdate because UPM with 7.15 has been having issues reading from Group Policy until this is run. Causing some Profile Issues.
    $Message = "Scheduling GPUPDATE command."; LogMessage $Message

    $GPUpdateCount = 0
    $UPMLogFile = 'C:\windows\system32\logfiles\userprofilemanager\Contoso.COM#' + $env:COMPUTERNAME + '_pm.log'

    $GPUpdateCount = 0
    Do {
        #Invoke-GPUpdate -Force -Target Computer
         & gpupdate /target:computer /force
         $GPUpdateCount++
    } Until ((Get-Content $UPMLogFile -tail 50 | Select-String -Pattern "Configuration value read from Policy").Count -gt 0 -or $GPUpdateCount -gt 9)

    If ($GPUpdateCount -gt 9) {
        $Message = "UPM not taking policies. Sending Text Alert."; LogMessage $Message

        #Send Alert to check UPM after restart before users really get logged in.
        $MessageText = "UPM not working on XenServer: " +  $env:computername
        $MessageSubj = "UPM not working on XenServer: " + $env:computername
        $XenAppTo = "person@contoso.com"

        Send-MailMessage -Body $MessageText -From $global:FromAddress -To $XenAppTo -Subject $MessageSubj -SmtpServer $global:SMTPServer -Credential $global:Cred
    }

    $Message = "Script complete."; LogMessage $Message

<# Removing Patch for 7.15
    #Patch RPM.DLL for 7.15 issue
    Rename-Item -Path "C:\Program Files (x86)\Citrix\System32\rpm.dll" -NewName "rpm.dll.orig" -Force 
    Copy-item -Path "C:\Contoso\XenApp-Upgrade\rpm.dll" -Destination "C:\Program Files (x86)\Citrix\System32"
    $Message = "Added patch for RPM.DLL for 7.15"; LogMessage $Message
#>
    #Clean-up Installation Files
    Get-ChildItem $global:UpgradeFiles | Remove-Item -Force
    Remove-Item -Path $global:UpgradeFiles -Force

    #It appears the 7.15 installation disables the Windows Search Service for some reason. Let's fix that.
    Set-Service WSearch -StartupType Automatic -ErrorAction SilentlyContinue

    #Final Reboot
    $Message = "Removing low risk files from clearance list"; LogMessage $Message
    Remove-LowRiskFiles
    $a = SetExecutionStatus -step 5 -PassFail "PASS"		
    $Message = "Performing final reboot"; LogMessage $Message
    $Message = "Rebooting machine in 15 seconds..."; LogMessage $Message

    $MessageText = "Successful install of XenApp on server: " +  $env:computername
    $MessageSubj = "Success: " + $env:computername

    Send-MailMessage -Body $MessageText -From $global:FromAddress -To $global:ToAddress -Subject $MessageSubj -SmtpServer $global:SMTPServer -Credential $global:Cred

    Start-Sleep -Seconds 15
    $Message = "Turning off Maintenance Mode..."; LogMessage $Message

    $MachineName = "Contoso\" + $env:computername
    Invoke-Command -ComputerName $global:StudioServer -ScriptBlock { param($RAA, $RMN) asnp citrix*; Get-BrokerMachine -AdminAddress $RAA -MachineName $RMN | Set-BrokerMachineMaintenanceMode -AdminAddress $RAA -MaintenanceMode $False } -ArgumentList ($global:AdminAddress, $MachineName)  
    Restart-Computer -force
    }

}

 

  • Like 1
Link to comment
  • 0

Thank you both so much. I see this is a very similar approach. But to be honest having a scheduled task and autologin is not our favorite solution, especially as we have a software distribution system like SCCM which is designed to do such tasks.

I will have a word with Citrix support and see what their best-practice is. If they can’t come up with a smooth solution I will use your scripts as example and build the workflow with autologin and scheduled tasks.

Link to comment
  • 0

Just to be clear, mine is pushed from our software distribution system. The scheduled tasks needed after the restarts are generated solely by the script to continue the process, and are removed on success or failure of the installation. Auto-logins are only created/updated during the script process. If you want a fully automated installation of the VDA, there has to be some Auto-logins during the process to ensure that all the requirements are installed and everything completes properly. 

Don't get me wrong, it's not an ideal solution, but it is the best I have found for my environment. The script could certainly be cleaned up as well since it handles both upgrades from 6.x as well as 7.xx. I just keep that it around for hysterical purposes. 

Link to comment
  • 0

For those that have used this script,  if you run it from your own machine, is there any indication if it completed successfully since the log is written on the remote machine or just have to wait long enough and check the installed vda version (we use controlup so simple enough) but thought id ask if there are any other indications.  this is a life saver if it works! thanks for the help

Edited by societyinsurance
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...