Function to trigger and application evaluation cycle after a specified time

This extension schedules a SCCM 2012 Application Evaluation Cycle task to be triggered in the specified amount of time.

On AppDeployToolkitExtensions.ps1 include:
#region Function Trigger-AppEvalCycle
Function Trigger-AppEvalCycle {
<#
.SYNOPSIS
Schedule a SCCM 2012 Application Evaluation Cycle task to be triggered in the specified time.
.DESCRIPTION
This function is called when the user selects to defer the installation. It does the following:
1. Removes the scheduled task configuration XML, if it already exists on the machine.
2. Creates a temporary directory on the local machine, if the folder doesn’t exists.
3. Creates an scheduled task configuration XML file on the temporary directory.
4. Checks if a scheduled task with that name already exists on the machine, if it exists then delete it.
5. Create a new scheduled task based on the XML file created on step 3.
6. Removes the scheduled task configuration XML.
7. Once the specified time is reached a scheduled task runs a SCCM 2012 Application Evaluation Cycle will start and it will trigger the installation/uninstallation to start if the machine is still part of the install/uninstall collection.
.PARAMETER Time
Specify the time, in hours, to run the scheduled task.
.EXAMPLE
Trigger-AppEvalCycle -Time 24
.NOTES
This is an internal script function and should typically not be called directly.
It is used to ensure that when the users defers the installation a new installation attempt will be made in the specified time if the machine is still part of the install/uninstall collection.
Version 1.0 - Anderson Cassimiro - 2015-July-14.
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[ValidateNotNullorEmpty()]
[int32]$Time = $true
)

Begin {
	## Get the name of this function and write header
	[string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
	Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header

	## Specify the scheduled task configuration in XML format
	[string]$schTaskRunDateTime = (((Get-Date).AddHours($Time)).ToUniversalTime()).ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ")
    [string]$schTaskAppEvalCycleCommand += "/NODE:$envComputerName /namespace:\\root\ccm PATH sms_client CALL TriggerSchedule <code>&quot;{00000000-0000-0000-0000-000000000121}</code>" /NOINTERACTIVE"
	[string]$xmlAppEvalCycleSchTask = @"

<?xml version=“1.0” encoding=“UTF-16”?>
<Task version=“1.2” xmlns=“http://schemas.microsoft.com/windows/2004/02/mit/task”>
<RegistrationInfo></RegistrationInfo>
<Triggers>
<TimeTrigger>
<StartBoundary>$schTaskRunDateTime</StartBoundary>
<Enabled>true</Enabled>
</TimeTrigger>
</Triggers>
<Principals>
<Principal id=“Author”>
<UserId>S-1-5-18</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>false</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT1H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context=“Author”>
<Exec>
<Command>WMIC.exe</Command>
<Arguments>$schTaskAppEvalCycleCommand</Arguments>
</Exec>
</Actions>
</Task>
"@
}
Process {
## Bypass if in NonInteractive mode
If ($deployModeNonInteractive) {
Write-Log -Message “Bypassing Function [${CmdletName}] [Mode: $deployMode].” -Source ${CmdletName}
Return
}

	[string]$schTaskAppEvalCycleName = $installName + '_AppEvalCycle'

    #  Specify the filename to export the XML to
    [string]$xmlSchTaskFilePath = "$dirAppDeployTemp\SchTaskAppEvalCycle.xml"
    
	## Delete the XML file if it exists
	If (Test-Path -Path "$xmlSchTaskFilePath" -PathType 'Leaf' -ErrorAction 'SilentlyContinue') {
		Remove-Item -Path "$xmlSchTaskFilePath" -Force -ErrorAction 'SilentlyContinue' | Out-Null
	}
	## Create Temporary directory (if required)
	If (-not (Test-Path -Path $dirAppDeployTemp -PathType 'Container' -ErrorAction 'SilentlyContinue')) {
		New-Item -Path $dirAppDeployTemp -ItemType 'Directory' -ErrorAction 'SilentlyContinue' | Out-Null
	}
	
	## Create a scheduled task to run to trigger an SCCM application evaluation cycle in the specified time
	Write-Log -Message "Create scheduled task to trigger an application evaluation cycle in $Time hours." -Source ${CmdletName}
	If (Get-ScheduledTask -ContinueOnError $true | Select-Object -Property 'TaskName' | Where-Object { $_.TaskName -eq "\$schTaskAppEvalCycleName" }) {
        Write-Log -Message "Scheduled task [$schTaskAppEvalCycleName] already exists." -Source ${CmdletName}
		Write-Log -Message "Delete Scheduled Task [$schTaskAppEvalCycleName]." -Source ${CmdletName}
		Execute-Process -Path $exeSchTasks -Parameters "/Delete /TN $schTaskAppEvalCycleName /F"		
	}

	## Export the scheduled task XML to file
	Try {
		[string]$xmlAppEvalCycleSchTask | Out-File -FilePath $xmlSchTaskFilePath -Force -ErrorAction 'Stop'
	}
	Catch {
		Write-Log -Message "Failed to export the scheduled task XML file [$xmlSchTaskFilePath]. <code></code>n$(Resolve-Error)" -Severity 3 -Source ${CmdletName}
		Return
	}
		
	## Import the Scheduled Task XML file to create the Scheduled Task
	[psobject]$schTaskResult = Execute-Process -Path $exeSchTasks -Parameters "/create /f /tn $schTaskAppEvalCycleName /xml <code></code>"$xmlSchTaskFilePath<code></code>"" -WindowStyle 'Hidden' -CreateNoWindow -PassThru
	If ($schTaskResult.ExitCode -ne 0) {
		Write-Log -Message "Failed to create the scheduled task [$schTaskAppEvalCycleName] by importing the scheduled task XML file [$xmlSchTaskFilePath]." -Severity 3 -Source ${CmdletName}
		Return
	}	
    
    ## Delete the XML file once execution is complete
	If (Test-Path -Path "$xmlSchTaskFilePath" -PathType 'Leaf' -ErrorAction 'SilentlyContinue') {
		Remove-Item -Path "$xmlSchTaskFilePath" -Force -ErrorAction 'SilentlyContinue' | Out-Null
	}
    
}
End {
	Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
}

}
#endregion

On Deploy-Application.ps1 include in the Pre-Installation phase before running any other code:

    ## Remove the AppEvalCycle scheduled task if it exists, this cleans up the machine in case the scheduled task was created before
	[string]$schTaskAppEvalCycleName = $installName + '_AppEvalCycle'
    Write-Log -Message "Check if Scheduled Task [$schTaskAppEvalCycleName] exists and delete if found." -Source "Trigger-AppEvalCycle"
	If (Get-ScheduledTask -ContinueOnError $true | Select-Object -Property 'TaskName' | Where-Object { $_.TaskName -eq "\$schTaskAppEvalCycleName" }) {
        Write-Log -Message "Scheduled Task [$schTaskAppEvalCycleName] found, deleting it." -Source "Trigger-AppEvalCycle"
		Execute-Process -Path $exeSchTasks -Parameters "/Delete /TN $schTaskAppEvalCycleName /F"
	}
    Else {
        Write-Log -Message "Scheduled Task [$schTaskAppEvalCycleName] not found, proceeding." -Source "Trigger-AppEvalCycle"
    }

If you want to trigger the SCCM 2012 Application Evaluation Cycle automatically after 24 hours after the user defers the installation you should modify the Defer actions on the AppDeployToolkitMain.ps1 file at the Function Show-InstallationWelcome:

			ElseIf ($promptResult -eq 'Defer') {
				Write-Log -Message 'Installation deferred by the user.' -Source ${CmdletName}
				$BlockExecution = $false
				
				Set-DeferHistory -DeferTimesRemaining $DeferTimes -DeferDeadline $deferDeadlineUniversal

# Create a scheduled task to trigger a CM Application Evaluation Cycle in 24 hours
Trigger-AppEvalCycle -Time 24

				#  Restore minimized windows
				$shellApp.UndoMinimizeAll() | Out-Null
				
				Exit-Script -ExitCode $configInstallationDeferExitCode
			}

Hello,

Would this defer for 24 hours completely or would SCCM still try to run this on it’s own scheduled schedule ?

So, by default, is there a process to trigger a rerun, especially for us that don’t use SCCM that has the evaluation cycle? Basically, in the default state, a user defers the installation, what triggers a re-run that will cause the welcome message to re-appear to the user for a deferral or continuation of the installation?

Fixed the grammar and added the whole thing into a code block.

#region Function TriggerAppEvalCycle
Function TriggerAppEvalCycle {
    <#
    .SYNOPSIS
    Schedule a SCCM 2012 Application Evaluation Cycle task to be triggered in the specified time.
    .DESCRIPTION
    This function is called when the user selects to defer the installation. It does the following:
    1. Removes the scheduled task configuration XML, if it already exists on the machine.
    2. Creates a temporary directory on the local machine, if the folder doesn’t exists.
    3. Creates an scheduled task configuration XML file on the temporary directory.
    4. Checks if a scheduled task with that name already exists on the machine, if it exists then delete it.
    5. Create a new scheduled task based on the XML file created on step 3.
    6. Removes the scheduled task configuration XML.
    7. Once the specified time is reached a scheduled task runs a SCCM 2012 Application Evaluation Cycle will start and it will trigger the installation/uninstallation to start if the machine is still part of the install/uninstall collection.
    .PARAMETER Time
    Specify the time, in hours, to run the scheduled task.
    .EXAMPLE
    TriggerAppEvalCycle -Time 24
    .NOTES
    This is an internal script function and should typically not be called directly.
    It is used to ensure that when the users defers the installation a new installation attempt will be made in the specified time if the machine is still part of the install/uninstall collection.
    Version 1.0 - Anderson Cassimiro - 2015-July-14.
    #>
    [CmdletBinding()]
    Param (
    [Parameter(Mandatory=$true)]
    [ValidateNotNullorEmpty()]
    [int32]$Time
    )
    
    Begin {
        ## Get the name of this function and write header
        [string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
        Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header
    
        ## Specify the scheduled task configuration in XML format
        [string]$schTaskRunDateTime = (((Get-Date).AddHours($Time)).ToUniversalTime()).ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ")
        [string]$schTaskAppEvalCycleCommand += "/NODE:$envComputerName /namespace:\\root\ccm PATH sms_client CALL TriggerSchedule <code>&quot;{00000000-0000-0000-0000-000000000121}</code> /NOINTERACTIVE"
        [string]$xmlAppEvalCycleSchTask = @"
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task 2">
<RegistrationInfo></RegistrationInfo>
<Triggers>
<TimeTrigger>
<StartBoundary>$schTaskRunDateTime</StartBoundary>
<Enabled>true</Enabled>
</TimeTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>S-1-5-18</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>false</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT1H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>WMIC.exe</Command>
<Arguments>$schTaskAppEvalCycleCommand</Arguments>
</Exec>
</Actions>
</Task>
"@
    }
    Process {
    ## Bypass if in NonInteractive mode
        If ($deployModeNonInteractive) {
        Write-Log -Message "Bypassing Function [${CmdletName}] [Mode: $deployMode]." -Source ${CmdletName}
        Return
        }
        [string]$schTaskAppEvalCycleName = $installName + '_AppEvalCycle'
    
        #  Specify the filename to export the XML to
        [string]$xmlSchTaskFilePath = "$dirAppDeployTemp\SchTaskAppEvalCycle.xml"
        
        ## Delete the XML file if it exists
        If (Test-Path -Path "$xmlSchTaskFilePath" -PathType 'Leaf' -ErrorAction 'SilentlyContinue') {
            Remove-Item -Path "$xmlSchTaskFilePath" -Force -ErrorAction 'SilentlyContinue' | Out-Null
        }
        ## Create Temporary directory (if required)
        If (-not (Test-Path -Path $dirAppDeployTemp -PathType 'Container' -ErrorAction 'SilentlyContinue')) {
            New-Item -Path $dirAppDeployTemp -ItemType 'Directory' -ErrorAction 'SilentlyContinue' | Out-Null
        }
        
        ## Create a scheduled task to run to trigger an SCCM application evaluation cycle in the specified time
        Write-Log -Message "Create scheduled task to trigger an application evaluation cycle in $Time hours." -Source ${CmdletName}
        If (Get-ScheduledTask -ContinueOnError $true | Select-Object -Property 'TaskName' | Where-Object { $_.TaskName -eq "\$schTaskAppEvalCycleName" }) {
            Write-Log -Message "Scheduled task [$schTaskAppEvalCycleName] already exists." -Source ${CmdletName}
            Write-Log -Message "Delete Scheduled Task [$schTaskAppEvalCycleName]." -Source ${CmdletName}
            Execute-Process -Path $exeSchTasks -Parameters "/Delete /TN $schTaskAppEvalCycleName /F"		
        }
    
        ## Export the scheduled task XML to file
        Try {
            [string]$xmlAppEvalCycleSchTask | Out-File -FilePath $xmlSchTaskFilePath -Force -ErrorAction 'Stop'
        }
        Catch {
            Write-Log -Message "Failed to export the scheduled task XML file [$xmlSchTaskFilePath]. <code></code>n$(Resolve-Error)" -Severity 3 -Source ${CmdletName}
            Return
        }
            
        ## Import the Scheduled Task XML file to create the Scheduled Task
        [psobject]$schTaskResult = Execute-Process -Path $exeSchTasks -Parameters "/create /f /tn $schTaskAppEvalCycleName /xml <code></code>"$xmlSchTaskFilePath<code></code>"" -WindowStyle 'Hidden' -CreateNoWindow -PassThru
        If ($schTaskResult.ExitCode -ne 0) {
            Write-Log -Message "Failed to create the scheduled task [$schTaskAppEvalCycleName] by importing the scheduled task XML file [$xmlSchTaskFilePath]." -Severity 3 -Source ${CmdletName}
            Return
        }	
        
        ## Delete the XML file once execution is complete
        If (Test-Path -Path "$xmlSchTaskFilePath" -PathType 'Leaf' -ErrorAction 'SilentlyContinue') {
            Remove-Item -Path "$xmlSchTaskFilePath" -Force -ErrorAction 'SilentlyContinue' | Out-Null
        }
    }
    End {
        Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
    }
}
#endregion
1 Like