问题
I have a really strange situation where the same command works when run manually from a Windows Server 2012 R2, but not from the Jenkins slave process running on the same server.
First, the output from the manual run, an admin PowerShell window:
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
Great. Now, the relevant snippet of Jenkins pipeline code:
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}
"""
}
}
}
}
And the output when running from 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
The Windows servers (Jenkins slave and web server) are not part of a domain, but have the same Administrator password, which seems to make the authentication work well.
For what it's worth, here's the winrm configuration for the Jenkins slave:
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
And from the webserver:
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
EDIT: I got it to work after a fashion. Firstly, on the Jenkins slave, I had to run:
winrm set winrm/config/client '@{AllowUnencrypted="true"}'
Then I changed the pipeline to:
powershell """
\$creds = Import-CliXml \$home\\creds.xml
Invoke-Command -computername ${SERVER} -scriptblock {iisreset /restart} -Authentication Basic -Credential \$creds
"""
where creds.xml
was a file previously generated with Get-Credentials | Export-CliXml creds.xml
.
That still doesn't explain why the behaviour is different between manual PowerShell and Jenkins slave. It's a bit of an annoying workaround, but at least I can proceed.
回答1:
You are probably hitting the remote execution script limitation from Jenkins (security is the cause here). You need to configure the Jenkins server to be able to run the script "normally" but you will always have to add the credentials.
The script you are running from powershell command line uses the default credentials for your win-cm8utd1qfnc\administrator
so the following will work as you wrote:
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
However, when running Powershell from Jenkins, a plugin by its nature, you are hitting the security-by-design limitation. You don't want to run "wild" scripts with your Administration account.
The most reasonable guide I have found on this topic was here (the following is excerpt from the page:
Executing Powershell scripts/commands remotely
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:
The following script is based on the above link, but I did not like the plain text used so I have decided to rewrite it to use Powershell's SecureString
First to store your admin password:
read-host -AsSecureString | ConvertFrom-SecureString | Out-File C:\<your_path>\securestring.txt
Then have the script:
# 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 }
回答2:
See this question: Remote Access with Powershell and Jenkins
Need to change service user from Local System to administrator.
来源:https://stackoverflow.com/questions/49740313/powershell-invoke-command-remote-works-manually-but-not-from-jenkins