windows-server-2016 任务计划程序未保存域用户以正常运行

windows-server-2016 任务计划程序未保存域用户以正常运行

任务计划程序 (TS) 没有保存以用户身份运行任务的域信息。

复制步骤:

在常规->安全选项中,它显示该任务将以哪个用户身份运行,旁边有“更改用户或组”按钮。

TS 允许我将用户更改为域用户,并在字段中显示“DOMAIN\USER”。

按下“确定”提示我输入“DOMAIN\USER”的密码,我照做了。

再次打开 TS 任务显示“DOMAIN\USER”的域部分已从 Run As 字段中删除。

当该任务运行时才不是以“DOMAIN\USER”身份运行。

预期行为

TS应该以用户身份运行任务,就像在 Server 2012r2 上一样。

操作系统详细信息

操作系统是 Windows Server 2016 标准版本 | 版本 10.0.14393 构建 14393 并且服务器是域的一部分。

编辑:在 Windows 10 中,它似乎做了同样的事情,但用户最终还是正确运行了。在导出的任务 XML 中,它看起来像这样:<UserId>DOMAIN\USER< /UserId> but now it looks like this:[numbers-with-a-lot-of-hypens-that-starts-with-a-letter] </UserId>

編輯-2:看起来 XML 设置中存储的是 Windows SID(安全标识符)。

答案1

事实证明,这是新机器上的额外权限,需要禁用它,然后任务计划程序才能正常运行。“SeDelegateSessionUserImpersonatePrivilege”就是罪魁祸首。

让我相信它是任务计划程序的原因是,任务计划程序改变了它在 XML 中保存用户的方式,它过去将其保存为“DOMAIN\USER”,但现在它将其保存为 SID(安全 ID),并且不会在任务计划程序的“RUN AS”部分显示域部分。

当我运行的时候,whoami /all我发现一个特权在新盒子上,但在旧盒子上却没有。

该特权是: SeDelegateSessionUserImpersonatePrivilege = 已禁用

删除此权限即可修复该问题。

因此,在 Windows Server 2016 std build 14393 上启用或删除权限 SeDelegateSessionUserImpersonatePrivilege 可修复任务未以任务计划程序中的存储用户身份运行的问题。

编辑:Windows Server 2016 任务计划程序可以正确运行第一次设置正确且无需编辑的任务,并且未选中“以最高权限运行”复选框,并且将来会启动这些任务。因此,如果您需要修改计划任务,您可能应该创建一个全新的任务,然后删除旧任务,而不是编辑现有任务。

答案2

@John Aho 的 GPO 解决方案对于那些可以完全控制组策略的人来说可能更好,但不适用于我所在的公司环境。经过数小时寻找解决方案后,我放弃了,并在 PowerShell 中创建了 2 个解决方法:

function Get-FullUsername {
    <#
    .SYNOPSIS
        Given a task, returns the executing username with a domain prefix.

    .DESCRIPTION
        Returns username, with domain prefix, of the account that is used to run the task.  
        Magic used: Exports the task to XML, parses and converts the SID. 
        Lame but some Windows 10, Windows Server 2016 and 2019 seem to not want to show the 
        full username.  If you have full access to GPO, then check out easier ways to get to this info.
        For more on the GPO solution, see SO post in the links.

        If using pipeline, I recommend first filter by the username you're looking 
        for: e.g. Where-Object { $_.Principal.UserId -eq john } 

    .PARAMETER Task
        Task Object. Specifically a object of Microsoft.Management.Infrastructure.CimInstance#MSFT_ScheduledTask

    .EXAMPLE
        $ObjTask = Get-ScheduledTask -TaskName "DR - Backup files"
        Get-FullUsername -Task $ObjTask

    .EXAMPLE
        Get-ScheduledTask -TaskName "Restart sentinel"  | Get-FullUsername

    .EXAMPLE
        Get-ScheduledTask | Where-Object { $_.Principal.UserId -eq "john" } | Get-FullUsername -Verbose

    .OUTPUTS
        [String] Domain\username.

    .LINK
        https://serverfault.com/questions/867351/windows-server-2016-task-scheduler-doesnt-save-domain-user-to-run-as-properly 

    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [object]$Task=$false
    )

    process
    {
        [xml]$XmlTask = Export-ScheduledTask -TaskName $Task.TaskName -TaskPath $Task.TaskPath
        $SID = $($XmlTask.Task.Principals.Principal.UserId)
        $ObjSID = New-Object System.Security.Principal.SecurityIdentifier ($SID)
        $ObjUser = $ObjSID.Translate( [System.Security.Principal.NTAccount] )
        $DomainUsername = $ObjUser.Value

        Write-Verbose "------------------------------------------------"
        Write-Verbose "TaskName: $($Task.TaskName)"
        Write-Verbose "TaskPath: $($Task.TaskPath)"
        Write-Verbose "SID: $SID"
        Write-Verbose "Returning Username: $DomainUsername "

        echo $DomainUsername
    }
}

function Get-ScheduledTasksByFullUsername {
    <#
    .SYNOPSIS
        Find tasks that are run under account given by FullUsername.

    .DESCRIPTION
        Currently can't search for tasks by a given username.  Even worse, the UserId returned
        by objects from Get-ScheduledTask is the shortname of a username (no domain).  If you have
        accounts in multiple domains running different tasks and you want to update the stored passwords
        for only 1 account in a domain, this is a problem.  Using this function you can get exactly 
        the tasks you need to update.

    .PARAMETER Tasks
        Optional array of Tasks Objects. Specifically a object of Microsoft.Management.Infrastructure.CimInstance#MSFT_ScheduledTask
        Accepts Tasks from pipeline so you can filter.

    .PARAMETER FullUsername
        Username with domain prefix (e.g. MAKESTUFFINC\john )

    .EXAMPLE
        Get-ScheduledTasksByFullUsername  -FullUsername "MAKESTUFFINC\john"

    .EXAMPLE
        Get-ScheduledTask | Get-ScheduledTasksByFullUsername -FullUsername "MAKESTUFFINC\john" -Verbose

    .EXAMPLE
        Get-ScheduledTask -TaskName "DR - Backup files" | Get-ScheduledTasksByFullUsername -FullUsername "MAKESTUFFINC\john"

    .EXAMPLE
        # Update all schedule tasks that are running under account by given credential. Credit to Jeremy Baumgartner.
        $TaskCredential = Get-Credential
        Get-ScheduledTasksByFullUsername -FullUsername $TaskCredential.UserName | Set-ScheduledTask -User $TaskCredential.UserName -Password $TaskCredential.GetNetworkCredential().Password

    .OUTPUTS
        Task objects - Specifically MSFT_ScheduledTask task objects.

    .LINK
        https://serverfault.com/questions/867351/windows-server-2016-task-scheduler-doesnt-save-domain-user-to-run-as-properly 

    .LINK
        https://nointerrupts.com/2018/10/18/update-scheduled-task-password-with-powershell/

    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false,ValueFromPipeline=$true)]
        [object[]]$Tasks=$false,
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateNotNullOrEmpty()]
        [string]$FullUsername=$false
    )

    begin {
        $ShortUsername = Split-Path $FullUsername -Leaf
        Write-Verbose "Full Username: $FullUsername"
        Write-Verbose "Short Username: $ShortUsername"
        $DefaultTasks = Get-ScheduledTask | Where-Object { $_.Principal.UserId -eq $ShortUsername } 
    }

    process {
        if (-not $Tasks) {
            # Use default tasks to search through
            Write-Verbose "Searching through tasks running under short username $ShortUsername."
            $Tasks = $DefaultTasks
        }

        ForEach($Task in $Tasks) {
            if ($Task) {
                Write-Verbose "================================================"
                Write-Verbose "Checking on $($Task.TaskName)"
                if (($(Get-FullUsername -Task $Task ) -eq $FullUsername)) {
                    # Let task live on in pipeline if matches criteria
                    $Task
                }
            }
        }
    }
}

相关内容