Pet*_*ter 26 powershell scheduled-tasks powershell-3.0 windows-server-2012
我一直在使用Powershell Scheduled Task Cmdlet在我们的服务器上创建计划任务.
如何使用此API选择"运行用户是否登录"?
我创建action
,trigger
,principal
和settings
对象,并将它们传递到Register-ScheduledTask
,如下图所示:
$action = New-ScheduledTaskAction -Execute foo.exe -Argument "bar baz"
$trigger = New-ScheduledTaskTrigger -Once -At $startTime -RepetitionInterval (New-TimeSpan -Minutes 1) -RepetitionDuration ([Timespan]::MaxValue)
$principal = New-ScheduledTaskPrincipal -UserId "$($env:USERDOMAIN)\$($env:USERNAME)" -LogonType ServiceAccount
$settings = New-ScheduledTaskSettingsSet -MultipleInstances Parallel
Register-ScheduledTask -TaskName $taskName -TaskPath "\my\path" -Action $action -Trigger $trigger -Settings $settings -Principal $principal
Run Code Online (Sandbox Code Playgroud)
当我创建这样的计划任务时,它默认为"仅在用户登录时运行".
这个问题显示了如何使用COM对象,这个使用schtasks.exe,但我如何使用*-ScheduledTask*
cmdlet?
Far*_*ley 38
我不喜欢或赞同当前评级最高的答案,因为你必须知道你的脚本中的凭据才能做到这一点,而不能从Packer或其他系统/配置自动化这样做.Aeyoun提到了一种更好/更合适的方法,但没有详细说明哪个是正确设置主体作为系统用户运行.
$action = New-ScheduledTaskAction -Execute foo.exe -Argument "bar baz"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1) -RepetitionDuration ([Timespan]::MaxValue)
$principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -MultipleInstances Parallel
Register-ScheduledTask -TaskName "tasknamehere" -TaskPath "\my\path" -Action $action -Trigger $trigger -Settings $settings -Principal $principal
Run Code Online (Sandbox Code Playgroud)
fou*_*ght 32
我也尝试使用 PowerShell 在 Windows Server 2019 上创建计划任务。所有答案都不起作用。似乎所有答案都确实有正确解决方案的一部分,但没有一个有完整的解决方案。虽然有些答案可能对某些人有用,但我确信,基于他们现有的系统、安全设置和其他因素,这真的完全是运气。在通过 PowerShell 创建一个非常简单的计划任务来收集一些应用程序遥测数据的过程中,我学到了很多东西。
\n因此,我完全修改了原来的答案,现在将带您逐步了解在几乎所有情况下创建计划任务(尤其是在服务器上)所需的步骤。如果事情像这样简单就好了cron
。
当大多数人想要创建计划任务时,特别是为了服务器/应用程序维护,或者只是定期运行某些任务,第一站就是 Windows 任务计划程序。提供了一个漂亮的 GUI(好吧,它可能会更好/现代化,但是嘿,至少它是一个 GUI),您可以在其中指定开始操作所需的所有详细信息。问题是,您无法自动化 GUI。我发现,GUI 正在幕后做一些事情,而 Microsoft 并没有直接告诉您它是如何做的(或者就此而言,它是如何不做的)。这可能会导致很多问题\xe2\x80\x94,包括任务未运行,甚至任务运行所用的用户帐户被锁定。
\n在组策略标题下有关作为批处理作业登录(SeBatchLogonRight
)的 Microsoft 文档中,Microsoft 指出,“当用户计划任务时,任务计划程序会自动授予此权限”。嗯,这个说法并不总是正确的。
当您使用任务计划程序 GUI 创建计划任务时,是的,如果计划任务配置为无论用户是否登录都运行并且用户没有作为批处理作业登录权限,则任务计划程序将分配该权利授予用户(除非更改默认值\xe2\x80\x94,请参阅上面引用的链接)。
\n但是,当使用 PowerShell ScheduledTask 模块的 cmdlet 创建计划任务时,不会发生这种自动用户权限分配。所以您必须手动执行此操作。执行此操作的 GUI 方法是使用本地安全策略 MMC(Microsoft 管理控制台)。当然,GUI 是在自动化场景中出现的,所以你的朋友将会是secedit.exe
。(对于我自己来说,我编写了 PowerShell 包装器secedit.exe
。)
因此,假设我们有一个计划任务,该任务将为您的服务器上运行的应用程序收集遥测数据,然后将该遥测数据发送到您的遥测收集服务,例如 New Relic 或 Data Dog。该任务将在用户帐户下运行CONTOSO\\AppTelemetry
。鉴于我们将在蓝/绿部署期间通过 PowerShell 以自动化方式创建计划任务,我们需要为此用户分配“作为批处理作业用户登录”权限。
secedit
分配用户权限这里的步骤非常简单:
\n让我们看一下实现这一点的代码。
\n您可以通过 CMD 或 PowerShell(桌面版或核心版,没关系)执行此操作。除非另有说明,所有示例都将在 PowerShell 中进行。
\nsecedit.exe /export /cfg secpol.inf /areas USER_RIGHTS\n
Run Code Online (Sandbox Code Playgroud)\n上述命令导出系统的安全策略,但仅导出包含用户权限分配信息的部分。如果您需要添加其他设置(例如注册表项),也可以通过安全策略来完成。secedit.exe
一般来说,请阅读有关安全策略的 Microsoft 文档。
SeBatchLogonRight
中现在我们需要创建一个新的安全策略模板,其中包含我们想要的新安全策略与当前安全策略的增量。我建议新的安全策略模板包含尽可能少的信息\xe2\x80\x94仅包含那些需要更改的设置。
\n人们可能会认为您所需要做的就是指定必须SeBatchLogonRight
包含用户的 SID,仅此而已。但如果你这么想,那你就错了。默认情况下分配有SeBatchLogonRight
一些用户(请参阅上面引用的 Microsoft 文档链接)。如果我们只是在新策略模板中将用户的 SID 分配给此权限,它将有效地替换系统安全策略中的现有值,而不是更新它。因此,由于我们正在进行附加更改,因此我们需要使用上面导出的系统安全策略中的现有值将此用户权限添加到我们的模板中,然后将我们的用户 SID 添加到列表中。
默认情况下,SeBatchLogonRight
分配有以下 SID:
SeBatchLogonRight = *S-1-5-32-544,*S-1-5-32-551,*S-1-5-32-559,*S-1-5-32-568\n
Run Code Online (Sandbox Code Playgroud)\n这些是一些“众所周知”的 SID,适用于某些标准 Windows 安全组,其用户应该拥有此权利。假设 SIDCONTOSO\\AppTelemetry
是S-1-5-21-0000000000-1111111111-2222222222-3333
。但是等等\xe2\x80\x94你是怎么得到的?
SeBatchLogonRight = *S-1-5-32-544,*S-1-5-32-551,*S-1-5-32-559,*S-1-5-32-568\n
Run Code Online (Sandbox Code Playgroud)\n(为什么我不简单地使用Get-ADUser
来获取用户帐户的 SID?有两个原因。首先,使用 来从 SID 获取用户帐户名并不那么简单Get-ADUser
。这是可以做到的,但是上面的代码更清楚。其次,也是最重要的是,并非所有用户都会Get-ADUser
在他们的计算机上安装。我以前的笔记本电脑安装了 Windows 远程服务器管理工具 (RSAT),所以我可以Get-ADUser
使用。如果没有安装 RSAT 或安装 Active Directory PowerShell 模块,Get-ADUser
不可用。上述 cmdlet 的工作不依赖于 Windows 和 .NET 框架之外的任何东西,如果您尝试获取用户帐户 SID 并使用 PowerShell 来执行此操作,根据定义,您很可能拥有两个先决条件。)
然后你可以简单地运行:
\nfunction ConvertTo-SecurityIdentifier {\n Param (\n [Parameter(Position = 0, Mandatory, ValueFromPipeline)]\n [string[]] $UsernameOrGroup\n )\n\n Process {\n foreach ($Name in $UsernameOrGroup) {\n $Account = New-Object -Type System.Security.Principal.NTAccount `\n -Argument $Name\n $Account.Translate([System.Security.Principal.SecurityIdentifier]).Value\n }\n }\n}\nSet-Alias -Name ConvertTo-SID -Value ConvertTo-SecurityIdentifier\n\nfunction ConvertFrom-SecurityIdentifier {\n Param (\n [Parameter(Position = 0, Mandatory, ValueFromPipeline)]\n [string[]] $SecurityIdentifier\n )\n\n Process {\n foreach ($SID in $SecurityIdentifier) {\n $Account = New-Object -Type System.Security.Principal.SecurityIdentifier `\n -ArgumentList $SID\n $Account.Translate([System.Security.Principal.NTAccount]).Value\n }\n }\n}\nSet-Alias -Name ConvertFrom-SID -Value ConvertFrom-SecurityIdentifier\n\n
Run Code Online (Sandbox Code Playgroud)\n现在我们有了 SID,我们可以创建一个安全策略模板(注意,有更好的方法来做到这一点\xe2\x80\x94我创建了 PowerShell cmdlet,让我以编程方式与这些 INF 文件交互,但我只是使用此处的文档)。请注意,在您导出的安全策略中,所有 SID 均以 为前缀*
,并且多个值以逗号分隔:
$SID = \'CONTOSO\\AppTelemtry\' | ConvertTo-SecurityIdentifier\n
Run Code Online (Sandbox Code Playgroud)\n标题说明了一切:
\n$NewPolicy = @\'\n[Unicode]\nUnicode=yes\n[Version]\nsignature="$CHICAGO$"\nRevision=1\n[Privilege Rights]\nSeBatchLogonRight = *S-1-5-32-544,*S-1-5-32-551,*S-1-5-32-559,*S-1-5-32-568,*S-1-5-21-0000000000-1111111111-2222222222-3333\n\'@\n\n$NewPolicy | Set-Content batchlogon.inf\n
Run Code Online (Sandbox Code Playgroud)\n标题再次说明了一切:
\nsecedit.exe /import /db batchlogon.sdb /cfg batchlogon.inf\n
Run Code Online (Sandbox Code Playgroud)\n这就是直接从 CLI将用户添加到登录作为批处理作业的全部内容。
\n现在,无论用户是否登录,用户都有权运行计划任务,我们需要创建一个无论用户是否登录都将运行的计划任务。您将看到许多指向许多问答答案的链接,这些链接表示您需要这个或那个 LogonType(特别是S4U
或ServiceAccount
)或以最高权限运行它,等等。不要听这些。这是(大部分)错误的。本节将概述创建无论用户是否登录都运行的计划任务所需的最少步骤。
首先是创建任务操作。计划任务由几个部分组成:
\n这些名称是相当不言自明的。我们将创建一个执行程序的计划任务操作。(还有其他操作类型。请参阅有关任务操作的文档。)
\n关于 Exec 操作需要了解的重要一点是,任务计划程序服务将生成一个实例来cmd.exe
运行所提供的程序。这意味着,如果您需要为程序指定参数,则需要确保遵循 CMD 命令的困难引用规则。(根据您的参数,这些引用规则可能需要大量测试才能确保命令按预期运行。即使在我的简单情况下,我也弄错了,任务似乎运行正确\xe2\x80\x94a0
退出代码\xe2\x80\x94但什么也没做!)查看cmd.exe /?
所有血淋淋的细节。此外,您还可以通过网络搜索找到很多信息。
让我们创建任务操作:
\nsecedit.exe /configure /db batchlogon.sdb\n
Run Code Online (Sandbox Code Playgroud)\n再次强调,请注意引用规则(即,如果您的文件路径中包含空格,或者您将一些 PowerShell 脚本传递给参数,-Command
而不是像-File
我在这里那样使用)。这里有几点需要指出:
pwsh.exe
PowerShell Core 来运行-NoLogo
避免打印启动 PowerShell 时出现的“横幅”。如果您这样做,它可以使重定向输出到日志文件更好。-ExecutionPolicy Bypass
指定此脚本将绕过系统上的任何当前执行策略。默认执行策略是Restricted
,除非指定完整路径,否则不允许任何脚本运行,并且不允许来自远程源的任何脚本。这个开关基本上保证了脚本的运行。它可能并不总是必要的,但如果您信任您正在安排的脚本,这也不会有什么坏处。-NoProfile
用于不加载用户的 PowerShell 配置文件(它与bash
Linux 操作系统上的用户配置文件的概念类似)。然而,也可以有全局配置文件脚本。除非您知道需要在 PowerShell 启动时运行配置文件脚本,否则添加此开关不会有什么坏处,并且很可能会防止错误。-NonInteractive
是一个非常重要的开关。基本上,这会阻止 Powershell 提示用户输入(例如,需要确认或用户输入的 cmdlet)。这也意味着如果您的脚本确实需要确认/用户输入,它将无法以非交互方式运行(即当用户未登录时)。-File
告诉 PowerShell 执行指定的文件。您也可以-Command
改为使用并传递一些“字符串化”PowerShell 代码。这是正确创建无论用户是否登录都将运行的计划任务的最重要步骤。事实上,这一步是配置一个计划任务无论用户是否登录都运行的必需步骤。
\n$TaskAction = New-ScheduledTaskAction -Execute \'powershell.exe\' `\n -Argument (\'-NoLogo -ExecutionPolicy Bypass -NoProfile -NonInteractive\' + `\n \'-File "C:\\Telemetry\\Send-ApplicationTelemetry.ps1"\')\n
Run Code Online (Sandbox Code Playgroud)\n让我们更详细地了解这些设置。
\n-Id
据我所知,这是一个自由格式的文本字段。任务计划程序 GUI 始终使用术语“作者”,但一般来说,您可以在此处使用您想要的任何内容。但需要注意的是,计划任务可以包含许多不同的操作(最多 32 个)。动作也有一个与之相关的“上下文”。任务计划程序自动将上下文分配给“作者”(对于简单的单操作任务)。文档似乎表明您可以为计划任务提供多个主体,并且主体的 ID 与操作的上下文相关,将确定操作将在哪个主体下执行。对于我们的单操作任务,我们将选择“作者”。
\n-UserId
或者-GroupId
任务可以在用户帐户或属于指定组的任何用户下运行。请注意,使用组时,只有该组的用户登录时才会运行计划任务。这是因为使用组 ID,您无法为任务指定密码。
\n在 99% 的情况下,您会-UserId
像我上面那样使用。它不需要是完全限定的用户 ID。请注意,我们不将密码与主体一起存储。那是稍后的事。
-LogonType
这可能是此 cmdlet 最容易被误解的参数之一。LogonType的文档非常好。可惜 PowerShell 帮助New-ScheduledTaskPrincipal
没有链接到它(我对此提出了反馈)。
基本上,您可能永远不需要关心登录S4U
类型。它可能在某些情况下相关(这就是它存在的原因),但对于几乎所有你想要做的事情,它可能不会。
ServiceAccount
如果计划任务将在( NT AUTHORITY\\LOCALSYSTEM
) LocalSystem
、NT AUTHORITY\\SYSTEM
(System
例如, ) 或NT AUTHORITY\\NETWORK
(NetworkService
由于我们的任务不是在系统帐户下运行,因此这不是我们想要的值。
您可能会猜到,Interactive
这意味着任务将在登录用户的上下文中运行。当任务需要启动 GUI 应用程序,或者任务操作是消息框以在系统上显示对话框时,这可能很有用。显然,这在我们的例子中没有用。还有InteractiveOrPassword
将此登录类型与我们想要的登录类型相结合的类型Password
。所以我不会进一步讨论它。
当然,现在Password
是我们想要的登录类型。这表示密码将与计划任务一起存储,该密码将用于登录用户(作为批处理作业),因此无论用户是否登录,任务都可以运行。是的,无论用户是否登录,这个值都会导致设置“运行”在任务计划程序 UI 中
-RunLevel
将此选项视为指定任务是否需要以提升的权限运行。当您运行命令(例如安装某些软件)时,用户访问控制 (UAC) 会启动并显示一个对话框,询问您是否允许该程序对您的计算机进行更改。您可能有Yes
一半时间都在点击而不阅读它。将此参数设置为Highest
就像单击Yes
UAC 对话框(或者只是使用提升的权限启动进程,即以管理员身份运行)。这是一件坏事,除非你知道你需要它。您应该始终从 开始Limited
,如果您发现计划任务无法在这些权限下运行,则提升。这就是我所做的。我从最小特权原则开始并使用Limited
运行级别。
您可以使用一个或多个触发器来触发计划任务。有不同的触发器类型。请参阅这些不同类型的文档。我要做一个非常简单的事情:
\n$TaskPrincipal = New-ScheduledTaskPrincipal -Id \'Author\' `\n -UserId \'CONTOSO\\AppTelemetry\' `\n -LogonType Password `\n -RunLevel Limited\n
Run Code Online (Sandbox Code Playgroud)\n我只想要一个从现在开始 5 分钟开始运行一次的任务,然后每 5 分钟运行一次。
\n如果我需要自定义一些其他任务设置,我可以使用New-ScheduledTaskSettingsSet
. 我对默认值很满意,所以我会跳过这个。但任务计划程序文档详细介绍了这一切,而且实际上,大部分设置都是不言自明的(除了 RunOnlyIfNetworkIsAvailable ,但它并不太难理解)。
现在您可能认为这是我们创建任务的地方,并且我们将在任务计划程序中看到它。错误的。但我们将创建一个计划任务对象。您必须执行此步骤,否则您的任务将无法正确注册\xe2\x80\x94最重要的是,无论用户是否登录,它都不会设置为运行。
\n$TaskTrigger = New-ScheduledTaskTrigger -Once -At ([DateTime]::Now.AddMinutes(5)) `\n -RepetitionInterval ( `\n [TimeSpan]::FromMinutes(5) `\n )\n
Run Code Online (Sandbox Code Playgroud)\n这里发生的只是我们正在创建一个计划任务实例。但它尚未添加到任务计划程序中的任务列表中。这就是所谓的注册,我们接下来就会这样做。如果您$Task
在执行上述命令后只是在 shell 中输入,您会发现任务名称和路径将为空。您甚至不能将其指定为调用的一部分New-ScheduledTask
。奇怪的是,这种情况又发生在注册过程中。那么我们来谈谈第二重要的部分:注册。
这是我可以简单地:
\n$Task = New-ScheduledTask -Description \'Collects application telemetry\' `\n -Action $TaskAction `\n -Principal $TaskPrincipal `\n -Trigger $TaskTrigger\n
Run Code Online (Sandbox Code Playgroud)\n但这会导致任务以交互方式运行(仅在用户登录时运行)。而这不是我们想要的。这就是我们创建上面的任务主体以及包含任务主体的任务实例的原因。
\n那么让我们注册我们的任务:
\n$Task = Register-ScheduledTask -TaskName \'Foo\' -TaskPath `\\` `\n -Action $TaskAction -Trigger $TaskTrigger `\n -User \'SomeUser\' -Password \'$uper53cr37\'\n
Run Code Online (Sandbox Code Playgroud)\n正如您所看到的,是的,您必须提供密码。有多种方法可以以自动化的方式安全可靠地执行此操作\xe2\x80\x94,但可惜的是,出于演示目的并不那么容易。(我最近看到这篇博文:如何使用秘密模块,其中概述了 PowerShell 秘密管理工具库中的另一个工具。)这里的重要部分是提供给Register-ScheduledTask
. 指定的用户必须与我们创建的主体中指定的用户相同。显然,该用户的密码必须正确。如果您使用不同的用户注册计划任务,任务主体将更新为使用注册任务时指定的用户帐户运行任务。(这里有记录。)
这就是调度任务运行的全部内容,无论用户是否使用 PowerShell 登录。但在我结束之前还有一个主题需要讨论:更新计划任务。
\n不管你相信与否,这并不像应有的那么简单。的语法Set-ScheduledTask
未正确记录。假设我需要在某个地方写入一个文件作为该计划任务的一部分,但写入该文件的代码不使用绝对路径。因此,我需要设置一个工作目录,但忘记这样做了。让我们修复操作并更新任务。
这是正确的做法。您可以使用不同的方法Set-ScheduledTask
,但我发现其他方法可以锁定用户帐户或根本不起作用(即给您一个错误)。这并不是说其他方法是错误的,但只是对于这种特殊的改变,这就是每次对我有用的方法:
$Task = $Task | Register-ScheduledTask -TaskName \'Collect Telemetry\' `\n -TaskPath \'\\Awesome App\' `\n -User \'CONTOSO\\AppTelemetry\' `\n -Password \'ShhD0ntT3ll4ny0n3!\'\n
Run Code Online (Sandbox Code Playgroud)\n同样,使用注册任务时使用的相同用户和密码非常重要。如果您使用不同的用户名/密码,则任务主体也将被更新。这可能不是您想要的。
\n这几乎就是在 Powershell 中使用计划任务的全部内容。
\nschtasks
调试帮助当您在通过 PowerShell 创建计划任务时遇到问题时,一种帮助自己的方法是通过 GUI 在本地计算机上创建类似的计划任务。然后,您可以使用该schtasks
命令查询您通过 GUI 创建的计划任务,并将其与您通过 PowerShell 创建的计划任务进行比较。使用schtasks
,例如:
$Task = Get-ScheduledTask -TaskName \'Collect Telemetry\' -TaskPath \'\\Awesome App\'\n$Task.Actions[0].WorkingDirectory = \'C:\\AwesomeApp\'\n$Task | Set-ScheduledTask -User \'CONTOSO\\AppTelemetry\' -Password \'ShhD0ntT3ll4ny0n3!\'\n
Run Code Online (Sandbox Code Playgroud)\n您还可以使用 .PowerShell 通过 PowerShell 获取计划任务的 XML 表示形式Export-ScheduledTask
。您可以在此处找到所有 PowerShell 计划任务 cmdlet 的文档。文档没问题。其中一些是误导性的;其中大部分内容是不完整的(即,它假设您除了 PowerShell cmdlet 本身之外还了解任务计划程序)。有关任务计划程序、其 COM 接口、XML 架构等的文档,可以在此处找到。
我希望这对某人有帮助,因为我花了很长时间才弄清楚这一切。主要是,“为什么这个任务没有做任何事情,即使它说它是成功的?” 或者“为什么帐户总是被锁定?” (登录类型、密码或用户权限分配错误,或三项全部错误!)
\nSha*_*evy 25
您需要删除$ principal并使用用户和密码注册任务:
Register-ScheduledTask -TaskName $taskname `
-TaskPath "\my\path" `
-Action $action `
-Trigger $trigger `
-User "$env:USERDOMAIN\$env:USERNAME" `
-Password 'P@ssw0rd' `
-Settings $settings
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
41195 次 |
最近记录: |