dahall / TaskScheduler

Provides a .NET wrapper for the Windows Task Scheduler. It aggregates the multiple versions, provides an editor and allows for localization.
MIT License
1.2k stars 191 forks source link

Task Updation fails #981

Closed jainudi48 closed 3 months ago

jainudi48 commented 6 months ago

Describe the bug Task fails to be updated for few of our customers with the error: ERROR [<UpdateTask>b__3 Intuit.Services.TaskScheduleManager.TaskSchedulerWapper+<>c__DisplayClass5_1] - Task update failed for the path : 'C:\Users\<username>\AppData\Roaming\Intuit\BU\Install\1.1.16\Intuit.BU.SelfUpdater.exe' error System.Runtime.InteropServices.COMException (0x80070534): (23,39):UserId:<MachineName>\<UserName> � Microsoft.Win32.TaskScheduler.V2Interop.ITaskFolder.RegisterTaskDefinition(String Path, ITaskDefinition pDefinition, Int32 flags, Object UserId, Object password, TaskLogonType LogonType, Object sddl) � Microsoft.Win32.TaskScheduler.TaskFolder.RegisterTaskDefinition(String path, TaskDefinition definition, TaskCreation createType, String userId, String password, TaskLogonType logonType, String sddl) � Microsoft.Win32.TaskScheduler.Task.RegisterChanges() � Intuit.Services.TaskScheduleManager.TaskSchedulerWapper.<>c__DisplayClass5_2.<UpdateTask>b__2() � Intuit.Common.Utilities.Wrapper.SafeCall(Action action, Action1 onErrorCallBack, Action onSuccessCallBack)`

We have written a software updater application that gets triggered with this task automatically after a regular interval. Another software of ours has to update this task some times. That updation is failing for a few customers out of thousands with above error. We know that task used to update fine on these machines earlier but it stopped updating tentatively after sept 2023.

I searched for the error code "0x80070534" to find some of the recent bugs in OS patches: https://borncity.com/win/2023/10/14/exchange-server-oct-2023-updates-fail-with-error-0x80070534/#:~:text=The%20error%20code%200x80070534%20stands,occurs%20only%20on%20single%20systems).

To Reproduce Steps to reproduce the behavior: Task Creation Sample Code:

private static bool CreateNewTaskToRunCallingExeDailyAtSpecificIntervals(string productId, TimeSpan timeSpan, string arguments)
{
    var buExePath = System.Reflection.Assembly.GetEntryAssembly().Location;
    var workingDirectory = Directory.GetParent(buExePath).FullName;
    var suExePath = Path.Combine(workingDirectory, @"D:\TestProjectToBeRunFromTaskScheduler\bin\Debug\TestProjectToBeRunFromTaskScheduler.exe");
    using (TaskService ts = new TaskService())
    {
        using (TaskDefinition td = ts.NewTask())
        {
            td.RegistrationInfo.Description = "Intuit Background updater for Product " + productId;
            //Creation/Modification triggers
            RegistrationTrigger registrationTrigger = new RegistrationTrigger()
            {
                Repetition = new RepetitionPattern(timeSpan, TimeSpan.Zero),
                Delay = TimeSpan.FromMinutes(1),//Delay for 15 minutes
            };
            //add logon trigger
            LogonTrigger logonTrigger = new LogonTrigger()
            {
                UserId = System.Security.Principal.WindowsIdentity.GetCurrent().Name,
                Delay = TimeSpan.FromMinutes(1),//Delay for 15 minutes
                Repetition = new RepetitionPattern(timeSpan, TimeSpan.Zero),
            };
            _ = td.Triggers.Add(registrationTrigger);
            _ = td.Triggers.Add(logonTrigger);
            _ = td.Actions.Add(new ExecAction(suExePath, arguments, workingDirectory));
            _ = td.Settings.DisallowStartIfOnBatteries = false;

            td.Principal.RunLevel = TaskRunLevel.Highest;

            //run as hidden
            td.Settings.Hidden = true;

            //set security options to run whether is user is logged in or not
            //td.Settings.RunOnlyIfLoggedOn = false;
            // Register the task in the root folder of the local machine
            _ = ts.RootFolder.RegisterTaskDefinition("TestTaskWithoutAdmin" + "\\" + productId + "_Updater", td);
        }
    }
    return true;
}

Task Update Sample Code:

public bool UpdateTask(string newExePath)
{
    return Wrapper.SafeCall(() =>
    {
        bool isUpdateTaskSucceed = true;
        var updaterTaskFolder = TaskService.Instance.GetFolder(Constants.SHEDULEDTASKS_FOLDER_NAME);
        if (updaterTaskFolder == null)
        {
            logger.Error("No task folder found with name : " + Constants.SHEDULEDTASKS_FOLDER_NAME);
            return false;
        }
        var updaterTaskCollection = updaterTaskFolder.Tasks;

        if (updaterTaskCollection == null || updaterTaskCollection.Count == 0)
        {
            logger.Error("No task to update for the path : '" + newExePath + "'");
            return false;
        }
        else
        {
            foreach (var updaterTask in updaterTaskCollection)
            {
                ExecAction action = null;
                Wrapper.SafeCall(() =>
                {
                    action = (ExecAction)updaterTask.Definition.Actions[0];
                    action.Path = newExePath;//Only update the exe path arguments should not be.
                    action.WorkingDirectory = Path.GetDirectoryName(newExePath);
                    updaterTask.RegisterChanges();

                }, (ex) =>
                {
                    logger.Error("Task update failed for the path : '" + newExePath + "'  error " + ex);
                    SentryWrapper.PushToSentrySync(ex, "Task_update_failed", ("newExePath", newExePath));
                    isUpdateTaskSucceed = false;
                });
                action?.Dispose();
                updaterTask?.Dispose();
            }

            updaterTaskCollection.Dispose();
        }
        return isUpdateTaskSucceed;
    }, (ex) =>
    {

        logger.Error("Task update falied " + ex);
        SentryWrapper.PushToSentrySync(ex, "Task_update_failed", ("newExePath", newExePath));
    });
}

Per the callstack that I pasted above in the description, I see that it fails in https://github.com/dahall/TaskScheduler/blob/273ccbea747e69373d3d192e694cd88291ca6759/TaskService/V2/TaskSchedulerV2Interop.cs#L371 But I can't see the interop component its calling.

Expected behavior The path of action i.e. Exe path should be updated in Task but task fails to update.

Screenshots

Environment (please complete the following information): Library version used: 2.10.1 Windows Versions where this issue was observed:

  1. Microsoft Windows 10.0.22631
  2. Microsoft Windows 10.0.19045
  3. Microsoft Windows 10.0.22621
  4. Microsoft Windows 10.0.22000

The windows version are just from 4 of our customers, but the issue may just not limited to these versions.

Additional context Add any other context about the problem here.

jainudi48 commented 6 months ago

@dahall / @spirifoxy / @filipnavara / @pashcovich - please help here. Thanks!

dahall commented 3 months ago

The key may be in the error message:

(23,39):UserId:<MachineName>\<UserName>

This tells me that there is a problem with credentials at the time this is called. I would check the results of this line of code to ensure you are getting an identity name that is valid on the system.

UserId = System.Security.Principal.WindowsIdentity.GetCurrent().Name,