<#
2020.04.17

Re-write to utilize the precommit.cmd file.  The precommit will now delete several scheduled tasks and other items before it reboots now that the TS will recovery autoamtically into the "Rollback" Section of the TS.

RollBack (Failed Upgrade) Remediation script, this is triggered by a scheduled task that you should set to run daily / start up.
It will check to see if a rollback has happened and then do the required actions to fix the CM Client.

#Checks for Failed Client and resets WMI


Ideas: Grab info from setup\mosetup\volatile to see at which % it failed.

Process:
Script Triggers & Waits 5 Minutes (Safe Guard, making sure the CM Client, if working is up and running)

Checks for Provisioning Mode and Removes
Clears out Running Task Sequence



#>
#Update $RegistryPath Value for your Environment. 
#Sorry, no error handling, it just tries, if it doesn't exist, you'll see some red. :-)
$RegistryPath = "HKLM:\SOFTWARE\WaaS"
$RegistryRollBack = "HKLM:\SYSTEM\Setup\Rollback"
$RegistryTemp = "HKLM:\SOFTWARE\RollBack"
$LogFile = "C:\Windows\ccm\Logs\RollBackRecovery.log"
$LastOSUpgradeFrom = Get-ItemPropertyValue -Path "$RegistryPath" -Name LastOSUpgradeFrom -ErrorAction SilentlyContinue
$LastOSUpgradeTo = Get-ItemPropertyValue -Path "$RegistryPath" -Name LastOSUpgradeTo -ErrorAction SilentlyContinue
$RegistryPathFull = "$RegistryPath\$LastOSUpgradeTo"
$ScriptName = $MyInvocation.MyCommand.Name
$WaaSKeyCurrent = get-item $RegistryPathFull 
$WaaSStage = $WaaSKeyCurrent.GetValue("WaaS_Stage")
$RollbackTriggered = $WaaSKeyCurrent.GetValue("RollbackTSTriggered") #This is Only created if the TS Runs the Rollback Section.
$TSIPUPackageID = $WaaSKeyCurrent.GetValue("IPUPackageID")
$CurrentTimeStamp = Get-Date -f s
[DateTime]$IPUTIME = ($WaaSKeyCurrent.GetValue('IPULastRun'))


#region: CMTraceLog Function formats logging in CMTrace style
        function CMTraceLog {
         [CmdletBinding()]
    Param (
		    [Parameter(Mandatory=$false)]
		    $Message,
 
		    [Parameter(Mandatory=$false)]
		    $ErrorMessage,
 
		    [Parameter(Mandatory=$false)]
		    $Component = "RollBackRecovery",
 
		    [Parameter(Mandatory=$false)]
		    [int]$Type,
		
		    [Parameter(Mandatory=$true)]
		    $LogFile
	    )
    <#
    Type: 1 = Normal, 2 = Warning (yellow), 3 = Error (red)
    #>
	    $Time = Get-Date -Format "HH:mm:ss.ffffff"
	    $Date = Get-Date -Format "MM-dd-yyyy"
 
	    if ($ErrorMessage -ne $null) {$Type = 3}
	    if ($Component -eq $null) {$Component = " "}
	    if ($Type -eq $null) {$Type = 1}
 
	    $LogMessage = "<![LOG[$Message $ErrorMessage" + "]LOG]!><time=`"$Time`" date=`"$Date`" component=`"$Component`" context=`"`" type=`"$Type`" thread=`"`" file=`"`">"
	    $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile
    }


function Disable-ProvMode
  {
  if ((Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\CCM\CcmExec' 'ProvisioningMode') -eq 'true') 
        {
        $ProvMode = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\CCM\CcmExec' 'ProvisioningMode' -ErrorAction SilentlyContinue
        CMTraceLog -Message  "ProvMode Status: $ProvMode" -Type 3 -LogFile $LogFile
        CMTraceLog -Message  "Removing Machine From Provisioning Mode and wait 30 seconds" -Type 2 -LogFile $LogFile
        Invoke-WmiMethod -Namespace root\CCM -Class SMS_Client -Name SetClientProvisioningMode -ArgumentList $false
        Start-Sleep -Seconds 30
        $ProvMode = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\CCM\CcmExec' 'ProvisioningMode' -ErrorAction SilentlyContinue
        if ($provmode -eq "True") 
            {
            CMTraceLog -Message  "ProvMode Status: $ProvMode" -Type 3 -LogFile $LogFile
            CMTraceLog -Message  "Removing Machine From Provisioning Mode" -Type 2 -LogFile $LogFile
            Invoke-WmiMethod -Namespace root\CCM -Class SMS_Client -Name SetClientProvisioningMode -ArgumentList $false
            }   
        Else 
            {
            $ProvMode = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\CCM\CcmExec' 'ProvisioningMode' -ErrorAction SilentlyContinue
            CMTraceLog -Message  "ProvMode Status: $ProvMode" -Type 1 -LogFile $LogFile
            }

        }
  Else 
        {
        $ProvMode = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\CCM\CcmExec' 'ProvisioningMode' -ErrorAction SilentlyContinue
        Write-Host "ProvMode Status: $ProvMode" -ForegroundColor Green
        CMTraceLog -Message  "ProvMode Status: $ProvMode" -Type 1 -LogFile $LogFile
        }
  }


#Create Function to Reset TS if running (The Hammer)
 function Reset-TaskSequence
    {
    CMTraceLog -Message "Checking for Stuck Task Sequence" -Type 1 -LogFile $LogFile
    $SMSTSLogLocation ="C:\Windows\ccm\Logs\SMSTSLog" 
    If (Test-Path $SMSTSLogLocation)
        {
        Write-host "TS Appears to be Running"
        CMTraceLog -Message "TS Appears to be Running" -Type 2 -LogFile $LogFile

        $OutputText = "Resetting CM Services to clear out TS - Takes about 3 minutes"
        CMTraceLog -Message  $OutputText -Type 2 -LogFile $LogFile
        Set-Service smstsmgr -StartupType manual
        Start-Service smstsmgr
        start-sleep -Seconds 5 
        #Dump Task Sequence Info from WMI
        if ($TSIPUPackageID)
            {
            if (Get-WmiObject -Namespace Root\CCM\SoftMgmtAgent -Class CCM_TSExecutionRequest | Where-Object {$_.ContentID -eq "$TSIPUPackageID"})
                {
                CMTraceLog -Message "Removing WMI CCM_TSExecutionRequest for $TSIPUPackageID" -Type 2 -LogFile $LogFile
                Get-WmiObject -Namespace Root\CCM\SoftMgmtAgent -Class CCM_TSExecutionRequest | Where-Object {$_.ContentID -eq "$TSIPUPackageID"} | Remove-WmiObject
                }
            }
        Else
            {
            if (Get-WmiObject -Namespace Root\CCM\SoftMgmtAgent -Class CCM_TSExecutionRequest)
                {
                CMTraceLog -Message "Removing WMI CCM_TSExecutionRequest for $TSIPUPackageID" -Type 2 -LogFile $LogFile
                Get-WmiObject -Namespace Root\CCM\SoftMgmtAgent -Class CCM_TSExecutionRequest | Remove-WmiObject
                Get-CimInstance -Namespace root/ccm -ClassName SMS_MaintenanceTaskRequests | Remove-CimInstance
                }
            }

        if ((Get-Process CcmExec -ea SilentlyContinue) -ne $Null) {Get-Process CcmExec | Stop-Process -Force}
        #stop-service ccmexec
        if ((Get-Process TSManager -ea SilentlyContinue) -ne $Null) {Get-Process TSManager| Stop-Process -Force}
        #Stop-Service smstsmgr  
        CMTraceLog -Message  "Starting Services Restart" -Type 1 -LogFile $LogFile
        Start-Sleep -Seconds 5
        Restart-Service -Name CcmExec -Force   
        Start-Service ccmexec
        Start-Sleep -Seconds 5
        Start-Service smstsmgr
        Start-Sleep -Seconds 20
        start-process "shutdown.exe" -ArgumentList "/a"
        if ((Get-Process TSManager -ea SilentlyContinue) -ne $Null) {Get-Process TSManager| Stop-Process -Force}
        Start-Sleep -Seconds 20
        if ((Get-Process CcmExec -ea SilentlyContinue) -ne $Null) {Get-Process CcmExec | Stop-Process -Force}
        Start-Sleep -Seconds 15
        Start-Service ccmexec
        start-process "shutdown.exe" -ArgumentList "/a"
        CMTraceLog -Message  "Services Reset Complete, 60 second pause" -Type 1 -LogFile $LogFile
        start-sleep -Seconds 60
        Start-Process -FilePath C:\windows\ccm\CcmEval.exe
        CMTraceLog -Message  "Trigger CCMEval" -Type 1 -LogFile $LogFile
        #Trigger  Machine  Policy  Update
        Invoke-WMIMethod -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000021}" |Out-Null
        Invoke-WMIMethod -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000022}" |Out-Null
        start-process "shutdown.exe" -ArgumentList "/a"
                
        #Check for  Setup  Running, if it is  KILL IT!  It shouldn't be running at this point, bad things might happen.
        if ((Get-Process "SetupHost" -ea SilentlyContinue) -eq $null){$SetupRunning = "False"}
        Else 
            {
            $SetupRunning = "True"
            $OutputText = "Setup Running - Stopping Now"
            CMTraceLog -Message  $OutputText -Type 2 -LogFile $LogFile
            Get-Process "SetupHost"| Stop-Process -Force
            start-sleep -Seconds 30
            start-process "shutdown.exe" -ArgumentList "/a"
            #After waiting 30 seconds, just double checking
            if ((Get-Process "SetupHost" -ea SilentlyContinue) -eq $null)
                {$SetupRunning = "False"}
                Else 
                {
                $SetupRunning = "True"
                $OutputText = "Setup Running - Stopping Now"
                CMTraceLog -Message  $OutputText -Type 2 -LogFile $LogFile
                Get-Process "SetupHost"| Stop-Process -Force               
                }
            }    
        $Global:ResetTSRan = "True"
        }
    Else 
        {
        $global:SMSTSRunning = "False"
        CMTraceLog -Message "No Stuck Task Sequence Found" -Type 1 -LogFile $LogFile
        }
    }

    Get-Process "SetupHost"| Stop-Process -Force

Function ConvertFrom-Logs
{
    [OutputType([PSObject[]])]
    Param
    (
        [Parameter(ValueFromPipeline)]
        [String] $string,
        [String] $LogPath,
        [string] $Date,
        [string] $LogComponent,
        [Int] $Bottom = $Null,
        [DateTime] $After
    )
    
    Begin
    {
    
        If ($LogPath) 
        {
            If (Test-Path -Path $LogPath)
            {
                $string = Get-Content -Raw -Path $LogPath
                $LogFileName = Get-Item -Path $LogPath |Select-Object -ExpandProperty name
            }
            Else
            {
                Return $False
            }
        }
    
        $SccmRegexShort = '\[LOG\[(?:.|\s)+?\]LOG\]'
        $SccmRegexLong = '(?im)((?<=\[LOG\[)((?:.|\s)+?)(\]LOG\]))(.{2,4}?)<(\s*[a-z0-9:\-\.\+]+="[_a-z0-9:\-\.\+]*")+>'

        $ErrorcodeRegex = '(?i)0x[0-9a-fA-F]{8}|(?<=\s)-\d{10}(?=\s)|(?<=code\s*)\d{1,}|(?<=error\s*)\d{1,}'
        $FilePathRegex = '(([a-zA-Z]\:)|(\\))(\\{1}|((\\{1})[^\\]([^/:*?<>"|]*))+)([/:*?<>"|]*(\.[a-zA-Z]+))'
    
        $StringLength = $string.Length    
        $Return = New-Object -TypeName System.Collections.ArrayList
    
    }
    Process
    {
        $TestLength = 500
        If ($StringLength -lt $TestLength)
        {
            $TestLength = $StringLength
        }
    
        #Which type is the log
        If ($StringLength -gt 5)
        {
            # SCCM Log Parshing
            If ([regex]::match($string.Substring(0,$TestLength),$SccmRegexShort).value,'Compiled')
            { 
                $SccmRegex = [regex]::matches($string,$SccmRegexLong)
        
                #foreach Line
                If (-not $Bottom -or $SccmRegex.count -lt $Bottom)
                {
                    $Bottom = $SccmRegex.count
                }
                For ($Counter = 1 ; $Counter -Lt $Bottom + 1; $Counter++)
                { 
                    $r = $SccmRegex[ $SccmRegex.count - $Counter]
                    $Errorcode = ''
                    $FilePath = ''
                    #get Message
                    $Hash = @{}
                    $Hash.Add('Message',$r.groups[2].value)
                    If($LogFileName)
                    {
                        $Hash.Add('LogFileName',$LogFileName)
                    }
                    If($LogPath)
                    {
                        $Hash.Add('LogPath',$LogPath)
                    }
                    #get additional information 
                    $parts = $r.groups |
                    Where-Object -FilterScript {
                        $_.captures.count -gt 1
                    } |
                    Select-Object -ExpandProperty captures

                    Foreach ($p in $parts)
                    {
                        If ($p.value -match '\w=')
                        {
                            $name = $p.value.split('=')[0].trim()
                            $value = $p.value.split('=')[1].replace('"','').Replace('>','').Replace('<','')
                           $Hash.Add($name, $value)
                        }
                    }
          
                    #convert to Datetime .net object
                    If ($Hash.Item('time') -ne $Null -and $Hash.Item('Date') -ne $Null)
                    {
                        $Hash.Add('TempTime', $Hash.Item('time'))
                        $Hash.Item('time') = [datetime] "$($Hash.Item('date')) $($Hash.Item('time').split('+')[0])"
                        If ($Hash.Item('time').gettype() -eq [datetime])
                        {
                            $Hash.Remove('Date')
                        }
                        Else
                        {
                            $Hash.Item('time') = $Hash.Item('TempTime')
                        }
                        $Hash.Remove('TempTime')
                    }
          
                    #get severity information
                    Switch ($Hash.Item('Type'))
                    {
                        0 
                        {
                            $Hash.Add('TypeName', 'Status')
                        }
                        1 
                        {
                            $Hash.Add('TypeName', 'Info')
                        }
                        2 
                        {
                            $Hash.Add('TypeName', 'Error')
                        }
                        3 
                        {
                            $Hash.Add('TypeName', 'Warning')
                        }
                       4 
                        {
                            $Hash.Add('TypeName', 'Verbose')
                        }
                        5 
                        {
                            $Hash.Add('TypeName', 'Debug')
                        }
                    }
          
                    #build object
                    If ($After -GT $Hash.Item('time') -and ([bool] $Hash.Item('time'))) 
                    {
                        $Counter = $SccmRegex.count
                    }
                    Try
                    {
                        [string] $Errorcode = [RegEx]::match($Hash['Message'],$ErrorcodeRegex 
                        ).value
                        $ErrorMSG = [ComponentModel.Win32Exception]::New([int]($Errorcode)).Message
                    }
                    Catch
                    {
                        $Errorcode = ''
                        $Error.removeat(0)
                    }
                    [string] $FilePath = [RegEx]::match($Hash['Message'],$FilePathRegex).value 
                    If ($Errorcode -ne '')
                    {
                        $Hash.Add('ErrorCode', $Errorcode)
                        $Hash.Add('ErrorMessage', $ErrorMSG)
                    }
                    If ($FilePath -ne '')
                    {
                        $Hash.Add('FilePath', $FilePath)
                    }
                    $TempObj = New-Object -TypeName PSobject -Property $Hash
                    $Return.add($TempObj)
                }
                [array]::Reverse($Return)
            }Else
            {
                Write-Warning -Message 'Not Sccm log format'
            }
        }
    }   
    End
    {
        Return $Return
    }
}


#Fail Safe... if TS Ran Rollback Section, don't run this script

if ($RollbackTriggered -eq $true)
    {
    Write-Output "Skipping rest of Script, Cleanup performed by Task Sequence"
    }
else
    {

    Write-Host "Computer Name: $env:computername" -ForegroundColor Green


    #Check to see if TS kicked back in (it will create a registry key if it does), and if it does, exit script.

    CMTraceLog -Message  "---Starting $ScriptName Script---" -Type 2 -LogFile $LogFile
    CMTraceLog -Message  "   Waiting 5 Minutes to make sure Client becomes active before triggering" -Type 1 -LogFile $LogFile
    Start-Sleep -Seconds 120
    CMTraceLog -Message  "   Waiting 5 Minutes, 3 Minutes Left in this long wait..." -Type 1 -LogFile $LogFile
    Start-Sleep -Seconds 120
    CMTraceLog -Message  "   Waiting 5 Minutes, 1 Minute left, so start paying attention" -Type 1 -LogFile $LogFile
    Start-Sleep -Seconds 60
    CMTraceLog -Message  "   Waiting 5 Minutes Completed! You Made it!  Continuing Recovery Process" -Type 1 -LogFile $LogFile

    $WaaSKeyCurrent = get-item $RegistryPathFull 
    if ($WaaSKeyCurrent.GetValue("RollbackTSTriggered") -eq $True)
        {
        CMTraceLog -Message  "Exiting Rollback Recovery" -Type 2 -LogFile $LogFile
        CMTraceLog -Message  "Found Registry Key for Rollback in TS" -Type 2 -LogFile $LogFile
        CMTraceLog -Message  "So the TS should have recovered to fail nicely." -Type 2 -LogFile $LogFile
        Exit
        }


    #Check when the IPU Last Run, if WaaS_Stage still Deployment_Startedand IPU over 20 hours, trigger Recovery.
     if ($WaaSKeyCurrent.GetValue("IPULastRun"))
        {
        $IPULastRun = Get-ItemPropertyValue "$RegistryPathFull" 'IPULastRun' -ErrorAction SilentlyContinue
        if ($IPULastRun -ne "")
            {
            $IPUDifference = ( [datetime](Get-Date -f 's')) - ([datetime]$IPULastRun)
            $IPUDifferenceHours = $IPUDifference.TotalHours
            $IPUDifferenceHoursRound = ([Math]::Round($IPUDifferenceHours,2))
            $IPUDifferenceDays = $IPUDifference.TotalDays}
            $IPUDifferenceDaysRound = ([Math]::Round($IPUDifferenceDays))
            if ($IPUDifferenceHoursRound -ge "20" -and $WaaSStage -eq "Deployment_Started")
                {
                $TriggerRecovery = $true
                Write-Host "Will Trigger Recovery: Machine still set to Deployment_Started and over 20 hours have passed" -ForegroundColor Red
                CMTraceLog -Message  "Will Trigger Recovery: Machine still set to Deployment_Started and over 20 hours have passed"  -Type 2 -LogFile $LogFile
                }
            Else {
                $TriggerRecovery = $false
                Write-Host "IPU Triggered $([datetime]$IPULastRun)"
                }
            }
    #Check if IPU was triggered Recently but Setup Engine is no longer running, then Trigger Recovery
     if ($WaaSKeyCurrent.GetValue("IPULastRun"))
        {
        $IPULastRun = Get-ItemPropertyValue "$RegistryPathFull" 'IPULastRun' -ErrorAction SilentlyContinue
        if ($IPULastRun -ne "")
            {
            $IPUDifference = ( [datetime](Get-Date -f 's')) - ([datetime]$IPULastRun)
            $IPUDifferenceHours = $IPUDifference.TotalHours
            $IPUDifferenceHoursRound = ([Math]::Round($IPUDifferenceHours,2))
            $IPUDifferenceDays = $IPUDifference.TotalDays}
            $IPUDifferenceDaysRound = ([Math]::Round($IPUDifferenceDays))
            $SetupProcess = get-process -Name "SetupHost" -ErrorAction SilentlyContinue
            if ($SetupProcess -eq $Null)
                {
                $TriggerRecovery = $true
                Write-Host "Will Trigger Recovery: Upgrade was recently triggered, but no Setup Engine running" -ForegroundColor Red
                CMTraceLog -Message  "Will Trigger Recovery: Upgrade was recently triggered, but no Setup Engine running"  -Type 2 -LogFile $LogFile
                }
            Else {
                $TriggerRecovery = $false
                Write-Host "IPU Triggered $([datetime]$IPULastRun)"
                }
            }


    #Start Actions

    #Force the default Windows LockScreen  images to be the actual LockScreen Image.  Update for your envirnment. 
    #Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization' -Name LockScreenImage -Value "C:\windows\Web\Screen\img100.jpg" -Force

    #Deleting the Key completely.  Customize this or the previous line for your envirnment. 
    Remove-ItemProperty -Path "HKLM:SOFTWARE\Policies\Microsoft\Windows\Personalization" -Name "LockScreenImage" -Force -ErrorAction SilentlyContinue
    if((Get-WmiObject win32_computersystem).username -eq $null) {Stop-Process -Name winlogon -Force -Verbose}



    $ClientVersion = Get-CimInstance -Namespace root/ccm -Class SMS_Client | select ClientVersion
    if ($ClientVersion)
        {
        Write-Host "CM Client Simple Check Pass" -ForegroundColor Green
        }
    Else   
        {
        #Remediate Client
    
        Write-Host "Client Bad"
        CMTraceLog -Message  "   Confirmed CCMClient WMI Currupt" -Type 3 -LogFile $LogFile
        New-ItemProperty -Path $RegistryPathFull -Name "RR_WMIReset" -Value $CurrentTimeStamp -force | Out-Null
        # stop wmi
        CMTraceLog -Message  "   Stop WMI Services" -Type 2 -LogFile $LogFile
        $depsvcs = get-service winmgmt -dependentservices | ? { $_.status -eq "running" } 
        stop-service "winmgmt" -force -erroraction ignore
        stop-service "wmiapsrv" -force -erroraction ignore

        # kill processes if any
        CMTraceLog -Message  "   Stop WMI Processes" -Type 2 -LogFile $LogFile
        get-process "wmiprvse" | Stop-Process -force -erroraction ignore 
        get-process "wmiapsrv" | Stop-Process -force -erroraction ignore 
        get-process "pretonservice" | Stop-Process -force -erroraction ignore 

        $wmistatus = get-service winmgmt 
        if ($wmistatus -eq "running") {
            get-process "wmiprvse" | stop-process -force -erroraction ignore 
            stop-service "winmgmt" -force -erroraction ignore 
            write-host "Stopping WMI again..." -ForegroundColor Green
            }
        # reset the WMI repository
        CMTraceLog -Message  "   Resetting WMI" -Type 2 -LogFile $LogFile
        winmgmt /resetrepository

        # run ccmrepair, since I couldn’t run it through WMI and it wouldn’t repair without the WMI repository being fixed
        write-host "Starting CCMRepair"
        CMTraceLog -Message  "   Triggering CCMRepair" -Type 2 -LogFile $LogFile
        c:\windows\ccm\ccmrepair.exe
        Start-Sleep -Seconds 120
        # start wmi
        CMTraceLog -Message  "   Starting WMI Services" -Type 2 -LogFile $LogFile
        start-service "winmgmt"  
        start-service "wmiapsrv" 
        $depsvcs | start-service -erroraction ignore

        Write-Host "Sleep 5 Minutes"
    
        Start-Sleep -Seconds 300
        $ClientVersion = Get-CimInstance -Namespace root/ccm -Class SMS_Client | select ClientVersion
        if ($ClientVersion)
            {
            Write-Host "Client OK"
            CMTraceLog -Message  "   WMI Repair Successful" -Type 2 -LogFile $LogFile
            }
        Else   
            {
            CMTraceLog -Message  "   WMI Repair Failed" -Type 3 -LogFile $LogFile
            }
        CMTraceLog -Message  "---Finished $ScriptName Script---" -Type 2 -LogFile $LogFile
        }





    #Check for Rollback Key and run section if Rollback detected


    if ((Test-Path "$RegistryRollBack") -or $TriggerRecovery -eq $true)
        {     

        if (test-path "C:\Windows\ccm\logs\SMSTSlog\smsts.log") {$smstslog = ConvertFrom-Logs -LogPath "C:\Windows\ccm\logs\SMSTSlog\smsts.log" -ErrorAction SilentlyContinue}
        else {$smstslog = ConvertFrom-Logs -LogPath "C:\Windows\ccm\logs\smsts.log" -ErrorAction SilentlyContinue}
        if ($smstslog -ne $null)
            {
            #Search for Unexpected Reboot in SMSTS Log and record it.
            Write-host "Searching for Unexpected Reboots in SMSTS.log" -ForegroundColor Green
            $string = $smstslog | Where-Object -FilterScript { $_.Message -match "STOP/SHUTDOWN control request received"}
            if ($string -ne $null)
                {
                #$Regex = [Regex]::new("(?<=LOG)(.*)(?=LOG)") 
                $Text = $string.Message
                $Time = $string.time
                [DateTime]$IPUTIME = ($WaaSKeyCurrent.GetValue('IPULastRun'))
                if ($time -gt $IPUTIME) 
                    {
                    Write-Host "$text at $Time" -ForegroundColor Yellow
                    CMTraceLog -Message  "$text at $Time" -Type 2 -LogFile $LogFile
                    write-host "  Rebooted after Starting IPU" -ForegroundColor Yellow
                    CMTraceLog -Message  "   Rebooted after Starting IPU" -Type 2 -LogFile $LogFile
                    write-host "  Started IPU: $IPUTIME" -ForegroundColor Yellow
                    CMTraceLog -Message  "   Started IPU: $IPUTIME" -Type 2 -LogFile $LogFile
                    Write-host "  Rebooted: $Time" -ForegroundColor Yellow
                    CMTraceLog -Message  "   Rebooted: $Time" -Type 2 -LogFile $LogFile
                    $RebootFound = $true
                    }
                }
            Else
                {
                Write-host "No Unexpected Reboots found in SMSTS.log" -ForegroundColor Green
                CMTraceLog -Message  "No Unexpected Reboots found in SMSTS.log" -Type 2 -LogFile $LogFile
                CMTraceLog -Message  "But you might want to check event logs" -Type 2 -LogFile $LogFile
                }
            }

        #Check if Running TS & Reset if Needed
        Reset-TaskSequence

        $RollbackPhase = Get-ItemPropertyValue -Path $RegistryRollBack -Name "Phase" -ErrorAction SilentlyContinue
        if ($RollbackPhase -ne "5")
            {
            if ((Test-Path $Registrytemp) -ne $True)
                {
                CMTraceLog -Message  "---Starting $ScriptName Script---" -Type 1 -LogFile $LogFile
                #Set OSRollBackRan key to 0, to know where in the script it was if a reboot should occur
                New-Item -Path $RegistryTemp –Force | out-null
                Set-ItemProperty -Path "$RegistryTemp" -Name "OSRollbackRan" -Value "0" -Force

                CMTraceLog -Message  "Starting CCM Disable ProvMode" -Type 1 -LogFile $LogFile
                Disable-ProvMode
                CMTraceLog -Message  "Starting CCM Service & CCMEval" -Type 1 -LogFile $LogFile
                Start-Process "C:\Windows\ccm\CcmEval.exe"
                CMTraceLog -Message  "Triggered CcmEval.exe" -Type 1 -LogFile $LogFile
                #Set OSRollbackRan key to 1, to know where in the script it was if a reboot should occur
                Set-ItemProperty -Path "$RegistryTemp" -Name "OSRollbackRan" -Value "1" -Force
                }
            if ((Get-ItemPropertyValue -Path "$RegistryTemp" -Name "OSRollbackRan") -eq "1")      
                {
                write-host "Starting OSRolbackRun Part 1"
                Write-Host "Waiting 5 Minutes, this is going to feel like forever" -ForegroundColor Gray
                CMTraceLog -Message  "Waiting 5 Minutes for CMClient to become active" -Type 1 -LogFile $LogFile
                Start-Sleep -Seconds 60
                Write-Host "Waiting 4 Minutes, right, what did I tell you, and you're only 20% done waiting" -ForegroundColor Gray
                CMTraceLog -Message  "Waiting 4 Minutes for CMClient to become active" -Type 1 -LogFile $LogFile
                Start-Sleep -Seconds 60
                Write-Host "Waiting 3 Minutes, still enough time to get coffee... run!!!" -ForegroundColor Gray
                CMTraceLog -Message  "Waiting 3 Minutes for CMClient to become active" -Type 1 -LogFile $LogFile
                Start-Sleep -Seconds 60
                Write-Host "Waiting 2 Minutes, congrats, you've reached the 60% complete mark for waiting" -ForegroundColor Gray
                CMTraceLog -Message  "Waiting 2 Minutes for CMClient to become active" -Type 1 -LogFile $LogFile
                Start-Sleep -Seconds 60
                Write-Host "Waiting 1 last Minute, almost there, hang in there, the end of the waiting is near" -ForegroundColor Gray
                CMTraceLog -Message  "Waiting 1 Minutes for CMClient to become active" -Type 1 -LogFile $LogFile
                Start-Sleep -Seconds 60
                Disable-ProvMode
                #Reset-TaskSequence
                #Set OSRollbackRan key to 2, to know where in the script it was if a reboot should occur
                Set-ItemProperty -Path "$RegistryTemp" -Name "OSRollbackRan" -Value "2" -Force
                }
            if ((Get-ItemPropertyValue -Path "$RegistryTemp" -Name "OSRollbackRan") -eq "2")      
                {     
                write-host "Starting OSRolbackRun Part 2"
                CMTraceLog -Message  "Triggering CM Hardware Inventory" -Type 1 -LogFile $LogFile
                write-host "Triggering CM Hardware Inventory" -ForegroundColor Gray
                Invoke-WMIMethod -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000001}" | Out-Null
                CMTraceLog -Message  "Triggering CM Machine Policy Retrieval Cycle" -Type 1 -LogFile $LogFile
                write-host "Triggering CM Machine Policy Retrieval Cycle" -ForegroundColor Gray
                Invoke-WMIMethod -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000021}" | Out-Null
                CMTraceLog -Message  "Triggering CM Machine Policy Evaluation Cycle" -Type 1 -LogFile $LogFile
                Invoke-WMIMethod -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000022}" | Out-Null
                write-host "Waiting 1 Minutes for Policy to Update" -ForegroundColor Gray
                CMTraceLog -Message  "Waiting 1 Minute for Policy to Update" -Type 1 -LogFile $LogFile
                Start-Sleep -Seconds 60
                CMTraceLog -Message  "Triggering CM Machine Policy Evaluation Cycle" -Type 1 -LogFile $LogFile
                Invoke-WMIMethod -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000113}" | Out-Null
                #Added Phase of Failure into OSUninstall_TellUsMore, because we don't have any dedicated values for RollBack
                if (Test-Path -Path $RegistryRollBack) {Set-ItemProperty -Path "$RegistryPathFull" -Name "RollbackPhase" -Value "$(Get-ItemPropertyValue -Path $RegistryRollBack -Name "Phase")" -Force}
                Set-ItemProperty -Path "$RegistryPathFull" -Name "WaaS_Stage" -Value "Deployment_RollBack" -Force
                #Set OSRollbackRan key to 3, to know where in the script it was if a reboot should occur, 3 basically means every time the script is triggered, it will do nothing, because it's already completed the required steps
                Set-ItemProperty -Path "$RegistryTemp" -Name "OSRollbackRan" -Value "3" -Force
                }
                if ((Get-ItemPropertyValue -Path "$RegistryTemp" -Name "OSRollbackRan") -eq "3")      
                {   
                write-host "Starting OSRolbackRun Part 3"
                if ($RebootFound -eq $true) {New-ItemProperty -Path $RegistryPathFull -Name "IPUReturnStatus" -Value "Unexpected Reboot during Upgrade"  -force | Out-Null} #Hijacking this Key until the others can be added to HWInv

                
                Set-ItemProperty -Path "$RegistryTemp" -Name "OSRollbackRan" -Value "4" -Force 
               
                Invoke-WMIMethod -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000021}" | Out-Null
                Invoke-WMIMethod -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000022}" | Out-Null
                CMTraceLog -Message  "---Exiting $ScriptName Script---" -Type 1 -LogFile $LogFile
                Start-Process "C:\Windows\ccm\CcmEval.exe"
                $CurrentTimeStamp = Get-Date -f s
                New-ItemProperty -Path $RegistryPathFull -Name "RR_ScriptComplete" -Value $CurrentTimeStamp -force | Out-Null
                Exit
                }
            }
        }
    else
        {
        if ($SetupDiagInfo -ne $null)
            {
            if ($SetupDiagInfo.GetValue('UpgradeStartTime') -ne 'CompatScanOnly'){Run-SetupDiag}
            }
        Write-Output "Did not meet requirements to run Rollback Script"
        Write-Output "Setup Engine Rollback Registry Key $RegistryRollBack Exist: $((Test-Path "$RegistryRollBack"))"
        Write-output  "Current WaaS Status: $((Get-ItemPropertyValue "$RegistryPathFull" 'WaaS_Stage' -ErrorAction SilentlyContinue))"
        if ((Test-Path "$RegistryTemp") -eq 'True')
            {
            Remove-Item -Path "$RegistryTemp" -Force
            }
        }
    }