Powershell Invoke-Command remote works manually, but not from Jenkins - powershell

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.

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 }

See this question: Remote Access with Powershell and Jenkins
Need to change service user from Local System to administrator.

Related

PowerShell Copy-Item and large files

I´m trying to copy a large file (around 180MB) to a remote server using PowerShell Copy-Item.
Copy-Item –Path "$sourcePath" –Destination "$destPath" -Force –ToSession $session
it works for files of arround 100MB, but i'm having issues with larger files.
This is the error:
Not enough storage is available to complete this operation.
The runspace state is not valid for this operation.
This is the configuration on server side.
Config
MaxEnvelopeSizekb = 256000
MaxTimeoutms = 60000
MaxBatchItems = 32000
MaxProviderRequests = 4294967295
Client
NetworkDelayms = 5000
URLPrefix = wsman
AllowUnencrypted = true
Auth
Basic = true
Digest = true
Kerberos = true
Negotiate = true
Certificate = true
CredSSP = false
DefaultPorts
HTTP = 5985
HTTPS = 5986
Any ideas?
Thanks in advance.

Set winrm/config/service RootSDDL via powershell

I need to set the RootSDDL programmatically via powershell for winrm/config/service
PS C:\Windows\system32> winrm set winrm/config/service #{RootSDDL="O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;RM)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)"}
Error: Invalid use of command line. Type "winrm -?" for help.
PS C:\Windows\system32>
winrm get winrm/config/service
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 [Source="GPO"]
Auth
Basic = true [Source="GPO"]
Kerberos = true
Negotiate = true
Certificate = false
CredSSP = false
CbtHardeningLevel = Relaxed
DefaultPorts
HTTP = 5985
HTTPS = 5986
IPv4Filter = * [Source="GPO"]
IPv6Filter = * [Source="GPO"]
EnableCompatibilityHttpListener = true [Source="GPO"]
EnableCompatibilityHttpsListener = true [Source="GPO"]
CertificateThumbprint
AllowRemoteAccess = true [Source="GPO"]
Resources
https://docs.ansible.com/ansible/latest/user_guide/windows_winrm.html#non-administrator-accounts
https://www.sevecek.com/Lists/Posts/Post.aspx?ID=280
you may set some of the options using Set-PSSessionConfiguration cmdlet:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-pssessionconfiguration?view=powershell-6
Or if you are interested in winrm/config/service settings, you may browse them via WSMan provider: cd wsmna:\localhost\service and use *-item cmdlets (Get-Item, Set-Item and etc).
If you would like to check other WSMan details in cd wsmna:\localhost\.
In powershell you may create a custom endpoit to connect and restrict PowerShell Language and cmdlets available. More details: https://learn.microsoft.com/en-us/powershell/scripting/learn/remoting/jea/overview?view=powershell-6
Hope it hels!
Best regards,
Ivan

How To Execute PowerShell via WinRM

Env:
Windows Server 2016 Standard Evaluation
version 1607(OSbuild 14393.2248)
my_powershell.ps1 is below.
echo $args
In local I can execute above script.
PS C:\Users\Administrator> .\echo_args.ps1 "aaaa"
aaaa
And I can execute command or exe files via winrm.
e.g)
PS C:\Users\Administrator> winrs -r:localhost -u:Administrator -p:MyPassword .\my.exe
But when I execute my_powershell.ps1 via WinRM(below), my_powershell.ps1 has been waiting, without doing something.
PS C:\Users\Administrator> winrs -r:localhost -u:Administrator -p:MyPassword .\echo_args.ps1
My WinRM config is below.
PS C:\Users\Administrator> winrm get winrm/config
Config
MaxEnvelopeSizekb = 500
MaxTimeoutms = 60000
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 = 2147483647
MaxShellRunTime = 2147483647
MaxProcessesPerShell = 2147483647
MaxMemoryPerShellMB = 2048
MaxShellsPerUser = 2147483647
Where should I chanage?
My command of winrm was wrong. When executing PowerShell via WinRm, I should specify to use PowerShell.exe.
So right command is below.
PS C:\Users\Administrator> winrs -r:localhost -u:Administrator -p:MyPassword powershell .\echo_args.ps1 "aaaa"

PowerShell DSC Pull Server returns HTTP 503 Service Unavailable

I'm using the PowerShell 5.0 September Preview to configure a PowerShell Desired State Configuration Pull Server on a Windows Server 2012 R2 virtual machine running on VMware Workstation. To perform the configuration of the DSC Pull Server, I am using a code snippet that I pulled off of the Microsoft PowerShell MSDN blog, which leverages the xPSDesiredStateConfiguration module's xDscWebService DSC resource.
When I attempt to test the OData endpoint for the DSC Pull Server, I receive a HTTP 503: Service Unavailable message. Any ideas on how to debug and fix this?
configuration DscWebService
{
param
(
[ValidateNotNullOrEmpty()]
[string] $CertificateThumbPrint = 'AllowUnencryptedTraffic'
)
Import-DSCResource -ModuleName xPSDesiredStateConfiguration;
WindowsFeature DSCServiceFeature
{
Ensure = 'Present';
Name = 'DSC-Service';
}
WindowsFeature WinAuth
{
Ensure = 'Present';
Name = 'web-Windows-Auth';
}
xDscWebService PSDSCPullServer
{
Ensure = 'Present';
EndpointName = 'PullSvc';
Port = 10100;
PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer";
CertificateThumbPrint = $CertificateThumbPrint;
ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules";
ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration";
State = 'Started';
DependsOn = '[WindowsFeature]DSCServiceFeature';
}
xDscWebService PSDSCConformanceService
{
Ensure = 'Present';
EndpointName = 'DscConformance';
Port = 10101;
PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCComplianceServer";
CertificateThumbPrint = 'AllowUnencryptedTraffic';
State = 'Started';
IsComplianceServer = $true;
DependsOn = #('[WindowsFeature]DSCServiceFeature', '[WindowsFeature]WinAuth','[xDSCWebService]PSDSCPullServer') ;
}
}
DscWebService -ComputerName dsc01.t.loc -OutputPath c:\dsc\PullServer -CertificateThumbPrint 00A2F55847C5523FE6CB0C2EE132C638339EA3A8;
Start-DscConfiguration -Wait -Verbose -Path c:\dsc\PullServer -Force;
a 503 Error usually indicates an issue with the apppool associated with a site. Run the following to see the state of your apppools
Get-ChildItem IIS:\AppPools

Powershell Remoting: Enable-WSManCredSSP fails with : This command cannot be executed because the setting cannot be enabled

I tried the following command to enable CredSSP:
Enable-WSManCredSSP -Role Client -DelegateComputer *.domain.local -Force
Enable-WSManCredSSP : This command cannot be executed because the
setting cannot be enabled.
How do I overcome this error? What am I doing wrong? What is the reason for this error?
Get-WSManCredSSP
The machine is not configured to allow delegating fresh credentials.
This computer is configured to receive credentials from a remote
client computer.
winrm get winrm/config
Config
MaxEnvelopeSizekb = 500
MaxTimeoutms = 60000
MaxBatchItems = 32000
MaxProviderRequests = 4294967295
Client
NetworkDelayms = 5000
URLPrefix = wsman
AllowUnencrypted = false
Auth
Basic = true
Digest = true
Kerberos = true
Negotiate = true
Certificate = true
CredSSP = true [Source="GPO"]
DefaultPorts
HTTP = 5985
HTTPS = 5986
TrustedHosts = *.XXX.local [Source="GPO"]
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 = false
Auth
Basic = false
Kerberos = true
Negotiate = true
Certificate = false
CredSSP = true [Source="GPO"]
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
Are you using GPO to create WinRM listeners?
You need to enable Allow Delegating Fresh Credentials for the WinRM client policy and add the SPN with WSMAN prefix.
There is also another way
I was working on this problem almost two weeks and now I know that sometimes you can have problem with command Enable-WSManCredSSP -Role client -DelegateComputer "my host"
That is because command has no access for registry editing even when you are running PowerShell as administrator. I think it's Microsoft bug and I will create a ticket for MS Support
However there is workaround and you can do it by this script
https://github.com/bolvua/Enable-WSManCredSSP