Pass arguments from one powershell script to another - powershell

I have one powershell script, like this one:
$ErrorActionPreference = "Stop"
$WORKDIR=$args[0]
Write-Host "Num of arguments given: " $args.Count
$AllArguments = ""
for ($i = 1; $i -lt $args.Count; $i += 2) {
$AllArguments = '$AllArguments $args[$i] "$($args[$i+1])"'
}
Write-Host "AllArguments: $($AllArguments)"
Write-Host "Starting .\someotherscript.ps1 in directory $($WORKDIR)"
pushd "$WORKDIR"
& Powershell -File .\someotherscript.ps1 $AllArguments
popd
Basically, this powershell script shoud start another powershell script, but without the first argument. So, e.g. when this script is started with .\firstscript.ps1 C:\some\dir -GiveMeParameter param1, then it should call the other script with the following parameters .\someotherscript.ps1 -GivMeParameter param1.
How to achieve that? At the moment I do not know how to solve this problem.

You can use multiple assignment to extract the first and remaining arguments by doing
$WORKDIR, $RemainingArgs = $args
I'm not sure why you're calling PowerShell again to run the script. You can just run the script directly and use splatting to pass the remaining args to the child script.
.\someotherscript.ps1 #RemainingArgs

Related

How to pass powershell hashtable from one script to another script in Jenkins Pipeline

In the local system I am able to execute powershell script by passing hashtable but the same is not working in Jenkins pipeline . I tried in Multiple ways but none of them working. Could you please help me on this.
I need solutions for couple of things
How can I pass a powershell hashtable to a groovy command
If above can't work how can I execute powershell script from powershell script block
stage('Passing Hashtable Test')
{
steps{
script{
powershell '''
write-host "hello Passing Hashtable "
$dict = #{
"firstKey" = "abc";
"User" = "Robot"
}
Write-host "Keys are present here : " + $dict.Keys
$env:WORKSPACE\PassingDictonary.ps1 -param1 "ThisIsStringValuePassed" -myHashTable $dict
'''
echo "priting outside script1 $dict" //how to get Powershell $dict variable here ?
//Atleast If I can send hashtable to ps1 file then its good . I tried this but not working
//OR At least If I can get Powershell variable outside of the block .. so that I would like to use a bat command by using -file parameter but I am not able to get value here.
}
}
}
This is my PassingDictionary.ps1 file code (this is working locally)
param (
[string]$param1 = "something",
[hashtable]$myHashTable
)
Write-host "Param1 value is : " + $param1
write-host "length of my Passed Hashtable is :" + $myHashTable.Count
foreach($k in $myHashTable.Keys)
{
Write-Host "Prining myHashTable Key is : => " + $k
Write-Host "Prining myHashTable Value is : => " + $myHashTable[$k]
}
At last I am able to execute powershell script from powershell block in Jenkins pipeline like below (this gives solution for my 2nd problem which mentioned above )
script {
powershell '''
PowerShell(. '.\\PassingDictonary.ps1' -param1 "ThisIsStringValuePassed"
-myHashTable $dict)
'''
}

remote Multiple invocations using Powershell

I appreciate you taking the time to read this.
My issue is as follows: I'm trying to create a program that uses powershell to do the following:
Take a table generated outside of powershell
Loop calls to a powershell script with the parameters from the table
The powershell script calls a special type of .cmd file and then runs commands on it that are located in a different shared location.
Now my problem is with the 3rd point.
I'm currently using the following to call my script (and the arguements are just hard coded to get it working, they'll be generated by the calls from step 2 later on):
powershell.exe -ExecutionPolicy Bypass -Command {invoke-command -file \\sharedlocation\test5.ps1 -computername server1121 -argumentlist 7058,Jason}
The inside of test5.ps1 is currently:
param(
[Parameter(Mandatory=$true)]
[string] $Var1,
[Parameter(Mandatory=$true)]
[string] $Var2
)
$CommandsPath = "\\sharedlocation\testcommands.cmd"
$path = "C:\"+$Var1+"\TOOLS\"+$Var2+"launchtool.cmd"
$scriptPath = [scriptblock]::Create($path)
$out | invoke-command {PARAM($MyArg) $scriptPath } -ArgumentList $CommandsPath
I've also tried using
$CommandsPath = "\\sharedlocation\testcommands.cmd"
$path = "C:\"+$Var1+"\TOOLS\"+$Var2+"\launchtool.cmd & " + $CommandsPath
$scriptPath = [scriptblock]::Create($path)
$out | invoke-command {$scriptPath }
I've also tried to call with hardcoded testcommands instead of them being in a file.
Now my problem is in both cases, it DOES run launchtool.cmd, but it doesn't pass the testcommands.cmd file.
However when on the machine i run
C:\7058\TOOLS\Jason\launchtool.cmd & \\sharedlocation\testcommands.cmd
It works fine.
Any ideas what I'm doing wrong?
Try, invoke-expression "cmd.exe /c C:\7058\TOOLS\Jason\launchtool.cmd & \sharedlocation\testcommands.cmd"
cmd.exe /c is my best way to ensure consistency between cmd and powershell
Is the UNC Path accessible from powershell? Copy the testcommands.cmd to a local path and try if it works!
$CommandsPath = "\\sharedlocation\testcommands.cmd"
if(Test-Path $CommandsPath)
{
$path = "C:\"+$Var1+"\TOOLS\"+$Var2+"\launchtool.cmd & " + $CommandsPath
$scriptPath = [scriptblock]::Create($path)
$out | invoke-command {$scriptPath }
}

Powershell accessing multiple parameters in a for loop

Hi I am very new to powershell and I am writing a script that accepts multiple parameters. These parameters are being accessed in a for loop inside the file.
It looks something like this
$numOfArgs = args.Length
for ($i=3; $i -le $numOfArgs; $i++)
{
write-host "folder: $args[$i]"
# does something with the arguments
}
However, the output gives me all the parameters as a whole instead of just one parameter specified in the array as an array element? Can someone tell me where is the mistake here? Thanks!
EDIT: Thanks Duncan to point this out, missing a $ in a variable.
Try this:
$numOfArgs = $args.Length
for ($i=3; $i -lt $numOfArgs; $i++)
{
write-host "folder: $($args[$i])"
# does something with the arguments
}
When placing a variable in a string, the variable is evaluated, not the entire expression. So by surrounding it with $() Powershell will evaluate the whole expression.
In other words, only $args was evaluated instead of $args[$i]
The preferred Powershell way is to use a bind parameter, like this;
param(
[Parameter(Mandatory=$true)][string[]]$Paths
)
# not sure why we're skipping some elements
$Paths | foreach-object { write-host "folder: $_" }
Which you can specify an array or arguments, like this;
.\myScript.ps1 -Paths c:\,c:\Users\,'c:\Program Files\'
This way it will work with -argument TAB completion and will even give you a brief usage using the get-help cmdlet.
get-help .\myscript.ps1
myScript.ps1 [-Paths] <string[]> [<CommonParameters>]

I want to make a powershell script that call another powershell script

So i have lets say a powershell script called CallMePlease.ps1
This script will take parameters / arguments and then does some a process. How do I append the arguments to the call when I call this script from MAIN.ps1? Code I have so far:
$ScriptPath = C:\Tmp\PAL\PAL\PAL\PAL.ps1
$Log 'C:\Users\k0530196\Documents\log.csv'
$ThresholdFile 'C:\Program Files\PAL\PAL\template.xml'
$Interval 'AUTO'
$IsOutputHtml $True
$HtmlOutputFileName '[LogFileName]_PAL_ANALYSIS_[DateTimeStamp].htm'
$IsOutputXml $True
$XmlOutputFileName '[LogFileName]_PAL_ANALYSIS_[DateTimeStamp].xml'
$AllCounterStats $False
$NumberOfThreads 1
$IsLowPriority $False
$cmd = "$ScriptPath\.\PAL.ps1"
Invoke-Expression "$cmd $Log $ThresholdFile $Interval $IsOutputHtml $HtmlOutputFileName $IsOutputXml $XmlOutputFileName $AllCounterStats $NumberOfThreads"
In the code that you posted you are missing several =s in your assignment statements. For instance this line:
$Log 'C:\Users\k0530196\Documents\log.csv'
Should be this:
$Log = 'C:\Users\k0530196\Documents\log.csv'
You will need to do that in all the instances where you are trying to assign a value to a variable.
I would do it like this:
. $cmd $Log $ThresholdFile $Interval $IsOutputHtml $HtmlOutputFileName $IsOutputXml $XmlOutputFileName $AllCounterStats $NumberOfThreads

Executing Powershell script from command line with quoted parameters

I am automating the build of a legacy MS Access application, and in one of the steps, I am trying to make an Access executable (.ADE). I have come up with the following code, which is stored in a file (PSLibrary.ps1):
Add-Type -AssemblyName Microsoft.Office.Interop.Access
function Access-Compile {
param (
[Parameter(Mandatory=$TRUE,Position=1)][string]$source,
[Parameter(Mandatory=$TRUE,Position=2)][string]$destination
)
Write-Output "Starting MS Access"
$access = New-Object -ComObject Access.Application
$access.Visible = $FALSE
$access.AutomationSecurity = 1
if (!(Test-Path $source)) { Throw "Source '$source' not found" }
if ((Test-Path $destination)) {
Write-Output "File '$destination' already exists - deleting..."
Remove-Item $destination
}
Write-Output "Compiling '$source' to '$destination'"
$result = $access.SysCmd(603, $source, $destination)
$result
Write-Output "Exiting MS Access"
$access.quit()
}
If I go into the PowerShell ISE and run the command below, then everything works fine, and the expected output is created:
PS C:>& "C:\Temp\PSLibrary.ps1"
PS C:>Access-Compile "C:\Working\Project.adp" "C:\Working\Project.ade"
However, I can't seem to generate the right hocus-pocus to get this running from the command line, as I would in an automated build. For instance,
powershell.exe -command "& \"C:\\Temp\\PSLibrary.ps1\" Access-Compile \"C:\\Temp\\Project.adp\" \"C:\\Temp\\Project.ade\""
What am I doing wrong?
For complex parameters, you can use Powershell's -EncodedCommand parameter. It will accept a Base64 encoded string. No escaping is needed for quotes, slashes and such.
Consider a test function that will print its parameters. Like so,
function Test-Function {
param (
[Parameter(Mandatory=$TRUE,Position=1)][string]$source,
[Parameter(Mandatory=$TRUE,Position=2)][string]$destination
)
write-host "src: $source"
write-host "dst: $destination"
}
Create command to load the script and some parameters. Like so,
# Load the script and call function with some parameters
. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""
After the command syntax is OK, encode it into Base64 form. Like so,
[System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes('. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""'))
You'll get a Base64 string. Like so,
LgAgAEMAOgBcAFQAZQBtAHAAXABDAGEAbABsAGkAbgBnAC0AVABlAHMAdAAuAHAAcwAxADsAIAAgAFQAZQBzAHQALQBGAHUAbgBjAHQAaQBvAG4AIAAiAHMAbwBtAGUAXABzAHAAZQBjAGkAYQBsADoAYwBoAGEAcgBhAGMAdABlAHIAcwA/ACIAIAAiAGAAIgBjADoAXABtAHkAIABwAGEAdABoAFwAdwBpAHQAaABcAHMAcABhAGMAZQBzACAAdwBpAHQAaABpAG4ALgBlAHgAdABgACIAIgA=
Finally, start Powershell and pass the encoded string as a parameter. Like so,
# The parameter string here is abreviated for readability purposes.
# Don't do this in production
C:\>powershell -encodedcommand LgAgA...
Output
src: some\special:characters?
dst: "c:\my path\with\spaces within.ext"
Should you later want to reverse the Base64 encoding, pass it into decoding method. Like so,
$str = " LgAgA..."
[Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($str))
# Output
. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""
PowerShell like Bash can take single or double quotes
PS C:\Users\Steven> echo "hello"
hello
PS C:\Users\Steven> echo 'hello'
hello
this can alleviate some of the headache, also I think you can use the literal backslashes without escaping.
To run PowerShell, choose
Start Menu Programs Accessories
Windows Powershell Windows Powershell