我遇到了一个非常奇怪的情况,当从 Windows Server 2012 R2 手动运行相同的命令时有效,但在同一台服务器上运行的 Jenkins 从属进程中却无效。
首先,手动运行的输出,一个管理员 PowerShell 窗口:
PS C:\Users\Administrator> whoami
win-cm8utd1qfnc\administrator
PS C:\Users\Administrator> Invoke-Command -computername web.sandbox.MUNGED.com -scriptblock {iisreset /restart}
Attempting stop...
Internet services successfully stopped
Attempting start...
Internet services successfully restarted
太棒了。现在,Jenkins 管道代码的相关片段:
pipeline {
stages {
stage('Deploy web') {
agent { label 'windows-server-2012' }
environment {
SERVER = 'web.sandbox.MUNGED.com'
}
steps {
powershell """
whoami
Invoke-Command -computername ${SERVER} -scriptblock {iisreset /restart}
"""
}
}
}
}
以及从 Jenkins 运行时的输出:
07:37:29 win-cm8utd1qfnc\administrator
07:37:29 [web.sandbox.MUNGED.com] Connecting to remote server web.sandbox.MUNGED.com failed with the following error message : Access is denied. For more information, see the
07:37:29 about_Remote_Troubleshooting Help topic.
07:37:29 + CategoryInfo : OpenError: (web.sandbox.MUNGED.com:String) [], PSRemotingTransportException
07:37:29 + FullyQualifiedErrorId : AccessDenied,PSSessionStateBroken
Windows 服务器(Jenkins 从属服务器和 Web 服务器)不属于域,但具有相同的管理员密码,这似乎使身份验证工作正常。
对于它的值(value),这里是 Jenkins 从站的 winrm 配置:
PS C:\Users\Administrator> winrm get winrm/config
Config
MaxEnvelopeSizekb = 500
MaxTimeoutms = 1800000
MaxBatchItems = 32000
MaxProviderRequests = 4294967295
Client
NetworkDelayms = 5000
URLPrefix = wsman
AllowUnencrypted = false
Auth
Basic = true
Digest = true
Kerberos = true
Negotiate = true
Certificate = true
CredSSP = false
DefaultPorts
HTTP = 5985
HTTPS = 5986
TrustedHosts = *
Service
RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
MaxConcurrentOperations = 4294967295
MaxConcurrentOperationsPerUser = 1500
EnumerationTimeoutms = 240000
MaxConnections = 300
MaxPacketRetrievalTimeSeconds = 120
AllowUnencrypted = true
Auth
Basic = true
Kerberos = true
Negotiate = true
Certificate = false
CredSSP = false
CbtHardeningLevel = Relaxed
DefaultPorts
HTTP = 5985
HTTPS = 5986
IPv4Filter = *
IPv6Filter = *
EnableCompatibilityHttpListener = false
EnableCompatibilityHttpsListener = false
CertificateThumbprint
AllowRemoteAccess = true
Winrs
AllowRemoteShellAccess = true
IdleTimeout = 7200000
MaxConcurrentUsers = 10
MaxShellRunTime = 2147483647
MaxProcessesPerShell = 4096
MaxMemoryPerShellMB = 8192
MaxShellsPerUser = 30
来自网络服务器:
PS C:\Users\Administrator> winrm get winrm/config
Config
MaxEnvelopeSizekb = 500
MaxTimeoutms = 1800000
MaxBatchItems = 32000
MaxProviderRequests = 4294967295
Client
NetworkDelayms = 5000
URLPrefix = wsman
AllowUnencrypted = false
Auth
Basic = true
Digest = true
Kerberos = true
Negotiate = true
Certificate = true
CredSSP = false
DefaultPorts
HTTP = 5985
HTTPS = 5986
TrustedHosts = *
Service
RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
MaxConcurrentOperations = 4294967295
MaxConcurrentOperationsPerUser = 1500
EnumerationTimeoutms = 240000
MaxConnections = 300
MaxPacketRetrievalTimeSeconds = 120
AllowUnencrypted = true
Auth
Basic = true
Kerberos = true
Negotiate = true
Certificate = false
CredSSP = false
CbtHardeningLevel = Relaxed
DefaultPorts
HTTP = 5985
HTTPS = 5986
IPv4Filter = *
IPv6Filter = *
EnableCompatibilityHttpListener = false
EnableCompatibilityHttpsListener = false
CertificateThumbprint
AllowRemoteAccess = true
Winrs
AllowRemoteShellAccess = true
IdleTimeout = 7200000
MaxConcurrentUsers = 10
MaxShellRunTime = 2147483647
MaxProcessesPerShell = 25
MaxMemoryPerShellMB = 1024
MaxShellsPerUser = 30
编辑:我很快就让它工作了。首先,在 Jenkins slave 上,我必须运行:
winrm 设置 winrm/config/client '@{AllowUnencrypted="true"}'
然后我将管道更改为:
powershell """
\$creds = Import-CliXml \$home\\creds.xml
Invoke-Command -computername ${SERVER} -scriptblock {iisreset /restart} -Authentication Basic -Credential \$creds
"""
其中 creds.xml
是之前使用 Get-Credentials | 生成的文件Export-CliXml creds.xml
.
这仍然不能解释为什么手动 PowerShell 和 Jenkins slave 之间的行为不同。这是一个有点烦人的解决方法,但至少我可以继续。
最佳答案
您可能遇到了 Jenkins 的远程执行脚本限制(安全是这里的原因)。您需要配置 Jenkins 服务器才能“正常”运行脚本,但您始终必须添加凭据。
您从 powershell 命令行运行的脚本使用您的 win-cm8utd1qfnc\administrator
的默认凭据,因此以下内容将按照您编写的方式工作:
PS C:\Users\Administrator> whoami
win-cm8utd1qfnc\administrator
PS C:\Users\Administrator> Invoke-Command -computername web.sandbox.MUNGED.com -scriptblock {iisreset /restart}
Attempting stop...
Internet services successfully stopped
Attempting start...
Internet services successfully restarted
但是,当从 Jenkins(本质上是一个插件)运行 Powershell 时,您会遇到设计安全限制。您不想使用您的管理帐户运行“狂野”脚本。
关于这个主题,我找到的最合理的指南是 here (以下为网页摘录:
远程执行 Powershell 脚本/命令
he above job creates a text file on the Jenkins server itself. To setup remote Powershell scripts we first need to configure Jenkins server for remote Powershell script execution. To enable remote Windows machine in to the WS-Man trusted list on Jenkins servers. Execute the below command on Powershell window on Jenkins server. The command will add all the remote machine to the trusted list.
Set-Item WSMan:\localhost\Client\TrustedHosts *
Along with the commands, we would also need to enable remote script execution also. To enable execution of Powershell scripts remotely execute the following command in Powershell window on Jenkins server.
Set-ExecutionPolicy RemoteSigned –Force
We will have to install a new plugin named EnvInject Plugin for transferring variables e.g. passwords.
Login to Jenkins and navigate to Manage Jenkins > Manage Plugins Click on the Available tab and Enter EnvInject in the filter box Select the plugin showing by name PowerShell Plugin Select Download now and install after restart
Creating a job to restart Windows time service:
On Jenkins interface, click New Item Enter Remote Powershell scripts for the job name. Select Freestyle project Tick This build is parameterized. Create following parameters Type: String Parameter Name: ServerIp/Hostname Description: Remote machine’s IP address. Type: String Parameter Name: UserName Type: Password Parameter Name: Password Now, Click Add Parameter list and select the Choice Parameter. Enter the options on new lines inside the Choices text box. Also,
provide description for the options mentioned:
以下脚本基于上述链接,但我不喜欢使用的纯文本,因此我决定重写它以使用 Powershell 的 SecureString
首先存储您的管理员密码:
read-host -AsSecureString | ConvertFrom-SecureString | Out-File C:\<your_path>\securestring.txt
然后有脚本:
# Configure build failure on errors on the remote machine similar to set -x on bash script
$ErrorActionPreference = 'Stop'
# Create a PSCredential Object using the "Username" and "Password" variables created on job
$password = Get-Content 'C:\<your_path>\securestring.txt' | ConvertTo-SecureString
$creddentials = New-Object System.Management.Automation.PSCredential -ArgumentList $env:UserName, $password
# It depends on the type of job you are executing on the remote machine as to if you want to use "-ErrorAction Stop" on your Invoke-Command.
Invoke-Command -ComputerName $env:Computer -Credential $credentials -ScriptBlock { Restart-Service -Name W32Time }
关于Powershell Invoke-Command 远程手动工作,但不是来自 Jenkins,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49740313/