When running powershell start-process from Jenkins pipeline with -RedirectStandardOutput "$PSScriptRoot\Script.output.txt"
locally it writes the output to the file.
When it runs in my jenkins pipeline i does not out anything to "$PSScriptRoot\Script.output.txt"
The pipeline runs on a windows node calls the script like this
stage("RunPowershellWith AnotherUser")
{
steps
{
script
{
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'c7e30cd4-4b6f-4387-8b2d-59f095bb5aae', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
powershell "${workspace}\\startAS.ps1 ${USERNAME} ${PASSWORD} "
}
}
}
}
The startAS.ps1 executes another powershell script with other credentials. The $PSScriptRoot\Script.output.txt is created, but there are no data in it. In fact i am in doubt if it is executed at all.
The powershell script looks like this
Param
(
[Parameter(Mandatory=$true, HelpMessage="Please provide username")][string] $username
,[Parameter(Mandatory=$true, HelpMessage="Please provide password")][string] $password
)
Write-Host "Running startAS"
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
Start-process C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
"$PSScriptRoot\update_gdpr_module_dt_tables.ps1" -Credential $credential -WorkingDirectory "C:\Windows\System32\WindowsPowerShell\v1.0\" -RedirectStandardError "$PSScriptRoot\Script.output.error.txt" -RedirectStandardOutput "$PSScriptRoot\Script.output.txt"
It Runs fine from my locale workstation
The above code did not even execute the script. My problem was to execute a powershell withcredentials from another user.
I figured it out with this example
stage('run powershell with credentials from jenkins') {
steps {
withCredentials([usernamePassword(credentialsId: 'c7e30cd4-4b6f-4387-8b2d-59f095bb5aae', passwordVariable: 'passVar', usernameVariable: 'userVar')]) {
powershell '''
& .\\myscript.ps1
'''
}
}
}
Trying to create a Powershell script that installs an application (.exe) with stored credentials (Clixml).
Everything works fine when using:
Start-Process -FilePath "C:\Users\$($env:USERNAME)\Downloads\Software\Software.exe" -ArgumentList '/s' -Credential $credentials
But I would like a more elegant solution:
$startprocessParams = #{
FilePath = "C:\Users\$($env:USERNAME)\Downloads\Software\Software.exe"
ArgumentList = '/s'
Credential = $credentials
Verb = 'RunAs'
PassThru = $true
Wait = $true
}
$proc = Start-Process #startprocessParams
if ($proc.ExitCode -eq 0) {
'Software installed!'
}
else {
"Fail! Exit code: $($Proc.ExitCode)"
}
This works perfectly without the Credential parameter, you then get the "enter credentials/UAC" popup that I would like to avoid. With the Credential parameter I get this error:
Start-Process : Parameter set cannot be resolved using the specified name parameters.
What am I missing here? Appreciate any advice and/or guidance.
EDIT:
I use the following line to import the credentials:
$credentials = Import-Clixml "C:\Users\$Env:USERNAME\AppData\Local\Apps\SOFTWARE\cred.xml"
The credentials is created with a standard:
Get-Credential | Export-Clixml "C:\Users\$Env:USERNAME\AppData\Local\Apps\SOFTWARE\cred.xml"
This works as it should.
you need to set the credentials as PSCredential.
have a look at this solution:
$username = "username"
$password = "password"
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList #($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))
Start-Process dnscrypt-proxy.exe -WorkingDirectory path_here -Credential ($credentials)
is it stored in PSCredential in the first place?
Start-Process : Parameter set cannot be resolved using the specified name parameters.
The error tells us the set of parameters used is incorrect. Checking the MSDN doc or Get-Help for Start-Process will show that -Credential can not be used with -Verb.
I am working with PowerShell 4.0 and I am trying to pass a string array as one of the parameters for an Invoke-Command -ScriptBlock in which I am calling another PowerShell script on a remote server. When I do this, the string array seems to get flattened so that it appears as a single string value, rather than a string array.
Listed below is the 1st script, which is being called by a Bamboo deployment server that provides the initial parameters.
In the Debug section, the $SupportFolders string array is iterated by the FlowerBoxArrayText function and it properly writes the two folder paths to the console, as expected.
24-Oct-2017 14:59:33 *****************************************************************************
24-Oct-2017 14:59:33 **** E:\SRSFiles\SRSOutput
24-Oct-2017 14:59:33 **** E:\SRSFiles\SRSBad
24-Oct-2017 14:59:33 *****************************************************************************
Here is the initial part of the 1st script file, showing the input parameters, the string array creation and where I am calling the remote script via Invoke-Command;
[CmdletBinding(DefaultParametersetName='None')]
param (
# Allows you to specify Install, Delete or Check.
[ValidateSet("Install", "Delete", "Check")][string] $Action = "Check",
# Allows you to specify the remote server name.
[string] $ComputerName = "None",
# Allows you to specify the username to use for installing the service.
[string] $Username = "None",
# Allows you to specify the password to use for installing the service.
[string] $Password = "None",
# Allows you to specify the location of the support folders for the service, if used.
[string] $SupportFoldersRoot = "None"
)
Function CreateCredential()
{
$Pass = $Password | ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential($Username, $Pass)
Return $Cred
}
Function FlowerBoxArrayText($TextArray, $TextColor="Yellow")
{
Write-Host "*****************************************************************************" -ForegroundColor $TextColor
foreach($TextLine in $TextArray)
{
IndentedText $TextLine $TextColor
}
Write-Host "*****************************************************************************" -ForegroundColor $TextColor
}
Function IndentedText($TextToInsert, $TextColor="Yellow")
{
Write-Host "**** $TextToInsert" -ForegroundColor $TextColor
}
$Credential = CreateCredential
[string[]] $ResultMessage = #()
[string] $Root = $SupportFoldersRoot.TrimEnd("/", "\")
[string[]] $SupportFolders = #("$Root\SRSOutput", "$Root\SRSBad")
#Debug
Write-Host "**** Starting debug in ManageAutoSignatureProcessorService ****"
FlowerBoxArrayText $SupportFolders -TextColor "Green"
Write-Host "**** Ending debug in ManageAutoSignatureProcessorService ****"
#End Debug
$ResultMessage = Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock {
param($_action,$_username,$_password,$_supportFolders) &"C:\Services\ManageService.ps1" `
-Action $_action `
-ComputerName DEV `
-Name DevProcessor `
-DisplayName 'DevProcessor' `
-Description 'DevProcessor' `
-BinaryPathName C:\Services\DevProcessor.exe `
-StartupType Manual `
-Username $_username `
-Password $_password `
-ServicePathName C:\Services `
-SupportFolders $_supportFolders `
-NonInteractive } -ArgumentList $Action,$Username,$Password,(,$SupportFolders)
if ($ResultMessage -like '*[ERROR]*')
{
FlowerBoxArrayText $ResultMessage -textColor "Red"
}
else
{
FlowerBoxArrayText $ResultMessage -textColor "Green"
}
Then, in the ManageService.ps1 script file on the remote server, I have the following;
[CmdletBinding(DefaultParametersetName='None')]
param (
# Allows you to specify Install, Delete or Check.
[ValidateSet("Install", "Delete", "Check")][string] $Action = "Check",
# Allows you to specify the name of the remote computer.
[string] $ComputerName = "None",
# Allows you to specify the service name.
[string] $Name = "None",
# Allows you to specify the service display name.
[string] $DisplayName = "None",
# Allows you to specify the service description.
[string] $Description = "None",
# Allows you to specify the path to the binary service executable file.
[string] $BinaryPathName = "None",
# Allows you to specify how the service will start, either manual or automatic.
[ValidateSet("Manual", "Automatic")][string] $StartupType = "Manual",
# Allows you to specify the domain username that the service will run under.
[string] $Username = "None",
# Allows you to specify the password for the domain username that the service will run under.
[string] $Password = "None",
# Allows you to specify the path to the service install scripts and service files on the remote server.
[string] $ServicePathName = "None",
# Allows you to specify the location of the support folders for the service, if used. The default value is an empty array
[string[]] $SupportFolders = #(),
# Disables human interaction, and allows all tests to be run even if they 'fail'.
[switch] $NonInteractive
)
Function CreateCredential()
{
$Pass = $Password | ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential($Username, $Pass)
Return $Cred
}
[bool] $OkToInstall = $False
[string[]] $ResultMessage = #()
#Debug
$ResultMessage = $ResultMessage += "[DEBUG] ***************************************"
$ResultMessage = $ResultMessage += "[DEBUG] SupportFolders: [$SupportFolders] ."
foreach ($Folder in $SupportFolders)
{
$ResultMessage = $ResultMessage += "[DEBUG] SupportFolders Item: $Folder."
}
$Count = #($SupportFolders).Count
$ResultMessage = $ResultMessage += "[DEBUG] SupportFolders Count: $Count ."
$ResultMessage = $ResultMessage += "[DEBUG] ***************************************"
#End Debug
The line,
$ResultMessage = $ResultMessage += "[DEBUG] SupportFolders: [$SupportFolders] ."
shows the following result from the $ResultMessage value that is returned to the calling script;
**** [DEBUG] SupportFolders: [E:\SRSFiles\SRSOutput E:\SRSFiles\SRSBad] .
Notice that the array is flattened out.
The foreach loop that follows also only prints out one value instead of two;
"E:\SRSFiles\SRSOutput E:\SRSFiles\SRSBad"
I have spent considerable time researching a solution but have yet to find an answer.
Any ideas?
EDIT 1 using #Bacon Bits suggestion;
$Options = #{'Action' = $Action
'ComputerName' = 'DEV'
'Name' = 'DevProcessor'
'DisplayName' = 'DevProcessor'
'Description' = 'Generate daily processes'
'BinaryPathName' = 'C:\Services\DevProcessor\DevProcessor.exe'
'StartupType' = 'Manual'
'Username' = $Username
'Password' = $Password
'ServicePathName' = 'C:\Services\DevProcessor'
'SupportFolders' = $SupportFolders
}
$ScriptBlock = {
param($Options)
& {
param(
$Action,
$ComputerName,
$Name,
$DisplayName,
$Description,
$BinaryPathName,
$StartupType,
$Username,
$Password,
$ServicePathName,
$SupportFolders,
$NonInteractive
)
&powershell "C:\Services\DevProcessor\ManageService.ps1 $Action $ComputerName $Name $DisplayName $Description $BinaryPathName $StartupType $Username $Password $ServicePathName $SupportFolders"
} #Options;
}
$ResultMessage = Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock $ScriptBlock -ArgumentList $Options
If I run the code modified as it is listed above, I still get the flattened array for $SuppportFolders and the ManageService.ps1 script trips up over parameters that have spaces, even though they are quoted when I assign them.
The option to completely wrap the code in ManageService.ps1, as opposed to simply calling the remote script is not really viable because the ManagedService.ps1 script is fairly extensive and generic so I can call it from over 30 automation scripts in my deployment server.
I believe what #Bacon Bits is suggesting would work if it was feasible to wrap the ManageService script.
To pass a single array, you can do this:
Invoke-Command -Session $Session -ScriptBlock $ScriptBlock -ArgumentList (,$Array);
However, that only works if you only need to pass a single array. It can all fall apart as soon as you start to pass multiple arrays or multiple complex objects.
Sometimes, this will work:
Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList (, $Array1), (, $Array2), (, $Array3);
However, it can be inconsistent in my experience. Sometimes it flattens the arrays out again.
What you can do is something similar to this answer.
{param($Options)& <# Original script block (including {} braces)#> #options }
Basically what we do is:
Wrap the script in a scriptblock that accepts a single hashtable as an argument.
Put all our arguments into the hashtable.
Use the passed hashtable as a splat variable.
So it would be something like:
$Options = #{
Action = 'Check';
ComputerName = 'XYZ123456';
Name = 'MyName';
.
.
.
}
$ScriptBlock = {
param($Options)
& {
[CmdletBinding(DefaultParametersetName='None')]
param (
# Allows you to specify Install, Delete or Check.
[ValidateSet("Install", "Delete", "Check")][string] $Action = "Check",
# Allows you to specify the name of the remote computer.
[string] $ComputerName = "None",
# Allows you to specify the service name.
[string] $Name = "None",
.
.
.
.
#End Debug
} #Options;
}
Invoke-Command -ComputerName RemoteServer -ScriptBlock $ScriptBlock -ArgumentList $Options;
Here's a trivial working example:
$Options = #{
List1 = 'Ed', 'Frank';
List2 = 5;
List3 = 'Alice', 'Bob', 'Cathy', 'David'
}
$ScriptBlock = {
param($Options)
& {
param(
$List1,
$List2,
$List3
)
"List1"
$List1
''
"List2"
$List2
''
"List3"
$List3
} #Options;
}
Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $Options;
Output:
List1
Ed
Frank
List2
5
List3
Alice
Bob
Cathy
David
Note that I tested this on PowerShell v5. I no longer have a system with PowerShell v4 to test on.
## To run the script
# .\get_status.ps1 -Hostname <host> -Service_Action <action> -Service_Name <name>
#$Hostname = "hostname"
#$Service_Action = "Get-Service"
#$Service_Name = "service_name"
param(
[string]$Hostname,
[string]$Service_Action,
[string]$Service_Name
)
$ScriptBlockContent = {
param($Service_Action, $Service_Name)
& $Service_Action $Service_Name
}
# user credentials
$Username = "username"
$Password = "password"
# To avoid Manual entry of Username and Password
$Secure_String = convertto-securestring $Password -asplaintext -force
$User_cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $Username, $Secure_String
# Create a Session
$pso = New-PSSessionOption -NoMachineProfile
$sess = New-PSSession -ComputerName $Hostname -SessionOption $pso -credential $User_cred
#Run a powershell script in the session.
Invoke-Command -Session $sess -ScriptBlock $ScriptBlockContent -ArgumentList $Service_Action, $Service_Name
# Remove session
Remove-PSSession $sess
To run the script:
.\<script_name>.ps1 -Hostname <host> -Service_Action <action> -Service_Name <name>
For ex: Service Action is- Get-Service, Stop-Service, Start-Service
and then Name
Command: Get-Service Servicename
I am getting an error:
Unexpected token in expression or statement on this line of code:
$ScriptBlockContent = {
param($Service_Action, $Service_Name)
$Service_Action $Service_Name # here is the error
}
You are passing your commands as strings to your function, so what you are syntactically doing with $Service_Action $Service_Name is to refer to two string objects in one line without any operator connecting them. That is the reason for the exception.
To tell powershell, that you want to execute a string as a command you have several options:
One option is to pass the commands as a single string to the Invoke-Expressioncmdlet:
Invoke-Expression "$Service_Action $Service_Name"
Alternatively you can use the call-Operator &, which also tells powershell to treat a command as string. In this case you cannot give cmdlet and arguments in a single string, but in two:
& $Service_Action $Service_Name
## To run the script
# .\get_status.ps1 -Hostname <host> -Service_Action <action> -Service_Name <name>
#$Hostname = "hostname"
#$Service_Action = "Get-Service"
#$Service_Name = "service_name"
param(
[string]$Hostname,
[string]$Service_Action,
[string]$Service_Name
)
$ScriptBlockContent = {
param($Service_Action, $Service_Name)
& $Service_Action $Service_Name
}
# user credentials
$Username = "username"
$Password = "password"
# To avoid Manual entry of Username and Password
$Secure_String = convertto-securestring $Password -asplaintext -force
$User_cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $Username, $Secure_String
# Create a Session
$pso = New-PSSessionOption -NoMachineProfile
$sess = New-PSSession -ComputerName $Hostname -SessionOption $pso -credential $User_cred
#Run a powershell script in the session.
Invoke-Command -Session $sess -ScriptBlock $ScriptBlockContent -ArgumentList $Service_Action, $Service_Name
# Remove session
Remove-PSSession $sess`enter code here`
I just stumbled upon this strange behavior of PowerShell Workflows and now I'm curious if anyone else can reproduce the behavior and maybe even explain it to me.
To describe the situation I've created two workflows that are almost identical. The only difference is that the PSCredential object is created in the workflow scope in the first and in the inlinescript in the second.
First I define the Workflows with the following code:
workflow WorkflowCred
{
[cmdletbinding()]
Param()
$Password = 'test' | ConvertTo-SecureString -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'test',$Password
InlineScript
{
Function Test-Verbose
{
[CmdletBinding()]
Param(
$Credential
)
Write-Output 'Start'
$Credential.GetType().FullName
Write-Verbose 'Testing'
Write-Output 'End'
}
Test-Verbose -Credential $Using:Credential
}
}
workflow InlineCred
{
[cmdletbinding()]
Param()
InlineScript
{
Function Test-Verbose
{
[CmdletBinding(
)]
Param(
$Credential
)
Write-Output 'Start'
$Credential.GetType().FullName
Write-Verbose 'Testing'
Write-Output 'End'
}
$Password = 'test' | ConvertTo-SecureString -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'test',$Password
Test-Verbose -Credential $Credential
}
}
Now to the strangeness. I can run the following Three commands without any problem:
WorkflowCred
InlineCred
WorkflowCred -Verbose
But if I run the second workflow with -Verbose my console freezes until I hit CTRL+C??
InlineCred -Verbose
I can see this behavior if all of the following conditions are true:
I run a function from within an InlineScript (in a PowerShell Workflow)
One of the parameters are given an object of the type PSCredential as value
The PSCredential object is created within the inlinescript
The function is Writing to the Verbose stream
I've tried this in PowerShell version 3 and 4.