Powershell Direct from Jenkins to Hyper-v Windows 10 - powershell

I have been trying to use Jenkins to automatically test VMs, which includes copying new code into the VM, or running certain commands from the host into the VM using Powershell Scripts.
However, I've been running into an error trying to using "Invoke-Command" or "New-PSSession" to a hyper-V Windows 10 VM in Jenkins Freestyle and Pipeline projects.
This is the environment Jenkins is running on:
Windows Specifications:
Edition: Windows Server 2019 Standard
Version: 1809
OS build: 17763.379
Jenkins ver. 2.190.1
Hyper-V Manager: 10.0.17763.1
This is the Powershell Scripts that I've written in a "Windows Powershell" Build Step in a freestyle project:
# Win10-Clean is currently running and the credentials are for the Win 10 VM.
$ErrorActionPreference = "Stop"
$VMName = "Win10-Clean"
$username = "username"
$pwd = "password"
$secpasswd = ConvertTo-SecureString $pwd -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("$username", $secpasswd)
Get-VM
Invoke-Command -Credential $mycreds -VMName $VMName -ScriptBlock {host}
I expect the output to be:
Running as SYSTEM
Building on master in workspace G:\ci_server_1\jenkins\workspace\powershell-testing-ground
[powershell-testing-ground] $ powershell.exe -NonInteractive -ExecutionPolicy ByPass "& 'C:\Windows\TEMP\jenkins6936256398230803297.ps1'"
Name State CPUUsage(%) MemoryAssigned(M) Uptime Status Version
---- ----- ----------- ----------------- ------ ------ -------
Win10-Clean Running 0 1854 5.14:10:42.5100000 Operating normally 8.3
PSComputerName : Win10-Clean
RunspaceId : 56151e46-5772-458f-8f11-9beba5491bc2
Name : ServerRemoteHost
Version : 1.0.0.0
InstanceId : fe09fc40-8434-4a7c-903b-b7b2c3f88506
UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture : en-US
CurrentUICulture : en-US
PrivateData :
DebuggerEnabled : True
IsRunspacePushed : False
Runspace : System.Management.Automation.Runspaces.LocalRunspace
Finished: SUCCESS
But what I got was this:
Running as SYSTEM
Building on master in workspace G:\ci_server_1\jenkins\workspace\powershell-testing-ground
[powershell-testing-ground] $ powershell.exe -NonInteractive -ExecutionPolicy ByPass "& 'C:\Windows\TEMP\jenkins233354859509300046.ps1'"
An error has occurred which Windows PowerShell cannot handle. A remote session might have ended.
At C:\Windows\TEMP\jenkins233354859509300046.ps1:7 char:1
+ Invoke-Command -Credential $mycreds -VMName Build_VM -ScriptBlock {ho ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (Build_VM:String) [], PSRemotingDataStructureException
+ FullyQualifiedErrorId : PSSessionStateBroken
Build step 'Windows PowerShell' marked build as failure
Finished: FAILURE
After referring to the official microsoft web page regrding this issue, the possible causes are:
VM exists but not running
Guest OS does not support PowerShell Direct
Powershell isn;t avaliable in the guest yet:
Operation system hasn't booted yet
OS can't boot correctly
Some boot time event needs user input
A bug in current builds where credentials must be explicitly passed with -Credential. Run Restart-Service -Name vmicvmsession as a work around.
However, from the output from Jenkins, the VM that I've been trying to connect to is "Running" and it is booted up properly.
Then, I've tried to "Invoke-Command" and "New-PSSession" into the VM from the host directly and I am able to connect to the VM.
I've also tried the same thing in a Jenkins installed in a Windows 10 machine instead of a windows server 2019 and everything works.
As for the user administrative information, the user logged in that is running Jenkins is both the "administrator" and the "hyper-v administrator".
These are some of the references I used to debug and I'm not able to find out what the problem is:
Remote Access With PowerShell and Jenkins, but I'm not able to figure out how exactly this works.
Running Powershell scripts using Jenkins
PowerShell: error executing command using Invoke-Command?
EDITS:
I found a way around this issue. Please mind that this isn't the definite solution but a work around.
Instead of running Invoke-Command -Credential $mycreds -VMName $VMName -ScriptBlock {host}, run this:
Invoke-Command -ComputerName "MyHostComputer" -ScriptBlock {Invoke-Command -Credential $mycreds -VMName $VMName -ScriptBlock {host}}
This command is essentially invoke-commanding into the host machine and use the host machine to run the Invoke-Command into the VM.
It is definitely not the perfect solution, but before anyone has a better solution I'll go with this for now.

Related

Executing CMD or EXE file using PSSession (remote powershell) caused Error 1603 access denied

I have the following script powershell command but it returns access denied. I assume the Error 1603 is caused by remote accessing the server. However, the $username has admin rights in the computer01 server.
To recheck if my hunch was right, I tried to test with the following and I got access denied:
Start-Process cmd -Credential $Cred
Update
The error was due to the $Cred . Removing the -Credential argument works fine.
End of Update
The commands have no problems executing directly in the computer01 machine using the cmd.exe.
I want to use cmd /c in this case as I need to get the real exit code from the SETUP.EXE installer.
See full script below:
$script = {
#Param(
# [String]$username,
# [String]$password
#)
# $Cred = New-Object System.Management.Automation.PSCredential ($username, $password)
$respfile = "$env:TEMP\test.resp"
echo 'key=value' > $respfile
$username = "$env:USERDOMAIN\$env:USERNAME"
Write-Host Hello $username
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
Start-Process cmd -Credential $Creds
#This command cannot be run due to the error: Access is denied.
# + CategoryInfo : InvalidOperation: (:) [Start-Process], #InvalidOperationException
# + FullyQualifiedErrorId : #InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
# + PSComputerName : computer01
# cmd /c "$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
runas /user:$Username "SETUP.EXE" /INSTALL -s /RESPFILE:"$respfile"
echo $LASTEXITCODE
# Error 1603
}
#$username = 'domain/user'
#$password = 'password'
$server = 'computer01'
$Creds = New-Object System.Management.Automation.PSCredential
$session = New-PSSession -ComputerName $server
#Invoke-Command -Session $session -Scriptblock $script -Argumentlist $username, $password
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds #updated based on #postanote advise
Remove-PSSession -ComputerName $server
I have found the following similar link install-remotely but do not want to use the ENTER-PSSession command. I do not want to exit the current PSSession and remotely join again in server just to install then exit.
Any suggestions how to use only PSSession and successfully executing installers in the remote server?
As one mentioned in the comments, you don't need cmd.exe. You can use the call/invocation operator - & - to specify that the next token on the line is a command:
& "$path\SETUP.EXE" /INSTALL -s /RESPFILE:$respfile
Of course, for this to work, the parameters to SETUP.EXE need to be correct (I don't know whether that's the case or not).
Never pass plain text passwords in scripts. It exposes you to uneeded risks.
Use proper secured credentials models.
• Working with Passwords, Secure Strings and Credentials in Windows PowerShell
• quickly-and-securely-storing-your-credentials-powershell
PowerShell remoting requires the use of an implicit (New-PSSession) or explicit (Enter-PSSession) session.
• About Remote Requirements
There are only a handful of cmdlets you can use as non-Admin ir run without PSRemoting enabled.
• Tip: Work Remotely with Windows PowerShell without using Remoting or WinRM
As noted in the Powershell Help file | MS Docs link above, with PSRemoting, you must be using an account that is an admin on the remote host.
In Windows OS proper, to install software, you must be an admin and running that in an admin session.
PowerShell runs in the context of the user who started it.
If you are trying to run in another user context, that is a Windows Security boundary, and you cannot do that without PowerShell natively, you'd need other tools like MS Sysinternals PSExec. See also:
Find-Module -Name '*Invoke*' | Format-Table -AutoSize
# Results
<#
Version Name Repository Description
------- ---- ---------- -----------
...
3.1.6 Invoke-CommandAs PSGallery Invoke Command as System/User on Local/Remote computer using ScheduleTask.
...
#>
Try this refactored option...
$script = {
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
$respfile = 'whatever this is'
& "SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
Write-Output $LASTEXITCODE
}
$server = 'computer01'
$session = New-PSSession -ComputerName $server
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds
Remove-PSSession -ComputerName $server
Details
# Get specifics for a module, cmdlet, or function
(Get-Command -Name Invoke-Command).Parameters
(Get-Command -Name Invoke-Command).Parameters.Keys
Get-help -Name Invoke-Command -Examples
# Results
<#
Invoke-Command -ComputerName server01 -Credential domain01\user01 -ScriptBlock {Get-Culture}
$s = New-PSSession -ComputerName Server02 -Credential Domain01\User01
$LiveCred = Get-Credential
Invoke-Command -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.exchangelabs.com/PowerShell -Credential $LiveCred -Authentication Basic
Invoke-Command -Session $s -ScriptBlock { Get-HotFix } -SessionOption $so -Credential server01\user01
Enable-WSManCredSSP -Delegate Server02
Set-Item WSMan:\Server02*\Service\Auth\CredSSP -Value $True
Invoke-Command -Session $s -ScriptBlock {Get-Item \\Net03\Scripts\LogFiles.ps1} -Authentication CredSSP -Credential Domain01\Admin01
#>
Get-help -Name Invoke-Command -Full
Get-help -Name Invoke-Command -Online
So, I was able to solve my problem.
1603 is the error thrown by the setup.exe.
Just to be sure, I manually executed first the following directly in the server using CMD and it was working!
$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'
I did a lot of testings. Researched and as mentioned from comments above, I did different ways to execute programs using powershell. I even used ACL to change ownership of installer directory/ files, switching to different user accounts (with different priviledges) but still getting access denied (including the Admin account).
It took days before I realized the difference in output file size of manual run in machine and the remote. The cause was the $respfile. It really is worth checking every possible reason/ scenario why there's access denied. Plus I cannot extract the setup.exe and its contents to troubleshoot.
The $respfile was created via powershell. I noticed the size created by powershell is doubled compared to a CMD size that was needed. With that, I assumed that the setup.exe reads file in UTF-8 format. I only know that it's working when triggered via CMD and not via powershell.
I suddenly bumped on this links differrent Powershell and CMD sizes and convert file content to CMD readable file - utf8. After converting the $respfile to UTF-8 format, I was able to run the exe successfully.
Hopefully, this can help others too!

Run remote Powershell session as version 2

I'm on a server that is running Powershell Version 2:
PS C:\> $PSVersionTable
Name Value
---- -----
...
PSVersion 2.0
I then create a new remote session to a different computer and connect to it:
$sess = New-PSSession -ComputerName {ComputerName} -Credential $credential
It returns me the result:
PS C:\> Invoke-Command -Session $sess -ScriptBlock { $PSVersionTable }
Name Value
---- -----
...
PSVersion 3.0
However, I need Powershell to be in Version 2 for my script so I enter a session (to make it easier). I then try to get Powershell to be Version 2:
C:\> Enter-PSSession -Session $sess
[{ComputerName}]: PS C:\> Powershell -Version 2
Windows Powershell
Copyright (C) 2009 Microsoft Corporation. All rights reserverd
And then it just hangs (or at least never lets me enter anything else into the console until I Ctrl-C).
I've also tried going through the Invoke-Command:
PS C:\> Invoke-Command -Session $sess -ScriptBlock { Powershell -version 2 }
and it does the same.
I've also tried to register a PSSessionConfiguration as per here: https://technet.microsoft.com/en-us/library/hh847899.aspx
PS C:\> Register-PSSessionConfiguration -Name PS2 -PSVersion 2.0
But I get:
Register-PSSessionConfiguration: a parameter cannot be found that matches parameter name 'PSVersion'.
Does anyone have any ideas of what I can try next?!
Thanks
On what machine did you run Register-PSSessionConfiguration?. Your computer or the "server"?
You need to make the configuration on the target server. That is what you will be running the hosted PSSessionConfiguration.
I just tried the steps in the technet article and it worked perfectly. My 2008 server remoted to my windows 7 machine running a 2.0 PSSessionConfiguration.
On target server/host:
Register-PSSessionConfiguration -Name PS2 -PSVersion 2.0
Then, on the client machine, reference the 'PS2' configuration.
$s = New-PSSession -ComputerName Server01 -ConfigurationName PS2
I take it that the following doesn't work either:
#Requires -version 2.0
Another kluge you could try is to create a scheduled task on the target and have the task fire off your script with Powershell.exe -version 2

Iterate through a list of VMs in Azure

i have about 10 VMs hosted on Auzre, i need to iterate through each of them and then execute a powershell script on each of them, lets say 'Set-Date'
whats the best way to connect to each VM, execute the ps script and then disconnect?
You can use PowerShell Remoting or custom scripts via extensions to execute PowerShell code on the remote VM.
For both solutions you get your list of VMs with the PowerShell command Get-AzureVM. Use a loop to iterate those VMs. I skip that part here because iterating are PowerShell basics.
1. PowerShell Remoting
For this you need PowerShell Remoting enabled on the remote VM and have an open port for PowerShell Remoting. Both is a default setting for new VMs.
Advantage: this solution is very handy for interactive sessions with a remote VM. Disadvantage of this solution is, that you need to authenticate to each VM and have to keep connected while execution.
With each VM you can do something like this. This is a shortened example where I have installed ADDS on the remote VM.
# Prepare credentials for remote session.
$secpasswd = ConvertTo-SecureString $AdminPassword -AsPlainText -Force
$credentialDC1 = New-Object System.Management.Automation.PSCredential ($AdminUsername, $secpasswd)
$EndpointDC = Get-AzureWinRMUri -ServiceName testlab-dc -Name dc1
#$EndpointDC = Get-AzureVM -ServiceName testlab-dc -Name dc1 | Get-AzureEndpoint -Name WinRmHTTPs
$psso = New-PSSessionOption -SkipCACheck
$sessionDC = New-PSSession -ComputerName testlab-dc.cloudapp.net -Port $EndpointDC.Port -Credential $credentialDC1 -UseSSL -SessionOption $psso
Invoke-Command -Session $sessionDC -ScriptBlock {
# Set-Date or other command
# or for example
# Install-WindowsFeature AD-Domain-Services
}
Remove-PSSession -Session $sessionDC
2. Custom Scripts via Extensions
Here you can upload a PowerShell file into your BLOB storage and then let execute that file on your VMs. Requirement is that the VM agent has to be installed on the VM. (Default for new VMs from the gallery.)
Advantage: you do not need to authenticate to each VM and you do not need to keep connect while execution.
Disadvantage: you have to prepare a separate PowerShell file to upload. Getting results is asynchronous.
Example:
# Upload PowerShell file
Set-AzureStorageBlobContent -Container extensions -File "Install-ADForest.ps1" -Blob "Install-ADForest.ps1"
# Install AD services and forrest
Get-AzureVM -ServiceName demoext -Name demoext |
Set-AzureVMCustomScriptExtension -ContainerName extensions -FileName "Install-ADForest.ps1" |
Update-AzureVM
The container has to exist. Create that container before you upload the file.

How to load Powershell ISE 3 with powershell v2 inside?

I just installed new powershell 3 on my Windows 7 machine and than I found out that new version of powershell doesn't work with Sharepoint 2010.
I also found a solution for this problem (here or here). But it only solves the problem for the standart powershell console. As we do most of the work through ISE, I wonder if it is possible to do the same thing in ISE?
I tried to add Version parameter, but ISE doesn't know it. I tried to type powershell -version 2 into ISE's console, but it didn't help.
If it would not be possible, I have another question: I need to use ISE with Sharepoint 2010, so how can I uninstall powershell 3 and new ISE?
This is a known issue when the Windows Management Framework 3.0 update is installed (it inlcudes PS 3.0) which, as it uses .net 4.0 makes all the SP2010 cmdlets (which are 3.5), incompatible.
The console application can accept the "-version 2" switch, but as pointed out this is not compatible with the ISE.
It is a known issue, another article suggests uninstalling the WMF update and re-booting the server, which I think is the only real answer to the last part of your question.
You can do this by creating new PSSession.
Register-PSSessionConfiguration -Name PS2 -PSVersion 2.0 –ShowSecurityDescriptorUI
# Please consult system admin when your run set-item and Enable-WSManCredSSP command
Set-Item wsman:localhost\client\trustedhosts -value * -Confirm:$false -Force
Enable-WSManCredSSP -Role Client –DelegateComputer * -Force
Enable-WSManCredSSP -Role Server -Force
# For test purpose
# Get-WSManCredSSP
# get-item wsman:localhost\client\trustedhosts
$cred = Get-Credential
$session = New-PSSession -ComputerName $env:COMPUTERNAME -authentication credssp -ConfigurationName PS2 -Credential $cred
Enter-PSSession $session
# 2.0 runtime
Add-PSSnapin microsoft.sharepoint.powershell
$web = Get-SPWeb http://SPSite/
$web.Url
Exit-PSSession
Unregister-PSSessionConfiguration -Name PS2
Disable-WSManCredSSP -Role Client
Disable-WSManCredSSP -Role Server
If you don't exit PSSession, you can run 2.0 runtime command from Powershell ISE 3.

How to invoke Powershell version 2 on remote computer via Invoke-Command

I'm trying to remotely setup websites on web servers using powershell. The web servers I'm attempting to configure are Windows Server 2008 R2 SP1 which has powershell v2 on it by default.
To make things easier, I'm using the Snapin "WebAdministration".
Whenever I attempt to invoke a the following command:
PS C:\p4\eacp4wireframe\ReleaseEngineering\DL_Powershell\Infrastructure\PowerShell\IIS> Invoke-Command -ComputerName web4 -Credential $admin -ScriptBlock {add-pssnapin WebAdministration; Get-Website}
No snap-ins have been registered for Windows PowerShell version 2.
+ CategoryInfo : InvalidArgument: (WebAdministration:String) [Add-PSSnapin], PSArgumentException
+ FullyQualifiedErrorId : AddPSSnapInRead,Microsoft.PowerShell.Commands.AddPSSnapinCommand
I get the dreaded "No snap-ins have been registered for Windows PowerShell version 2" error.
Logically, I check the version of powershell by running this command:
PS C:\ Invoke-Command -ComputerName web4 -Credential $admin -ScriptBlock {(Get-Host).version}
Major Minor Build Revision PSComputerName
----- ----- ----- -------- --------------
1 0 0 0 web4
I can remotely login to the web server and run the Powershell commands fine.
Any ideas?
That version number is the version number of the host implementation used by remoting e.g.:
C:\PS> Invoke-Command . {Get-Host | Get-Member}
TypeName: System.Management.Automation.Internal.Host.InternalHost
You have to be using at least the PowerShell 2.0 engine because that is when PowerShell Remoting was introduced. It is more likely that you're invoking the 64-bit PowerShell remoting endpoint and you have snapins that either haven't been registered for the 64-bit PowerShell or won't run in 64-bit PowerShell (or vice versa).
If you need to invoke the 32-bit remoting endpoint try this:
C:\PS> Invoke-Command . {[intptr]::size} -ConfigurationName Microsoft.PowerShell32
4