I'm trying to figure out the correct way to request parameters in a PowerShell script without using a function. With the following sample script I get an error if I don't include the Param within the function.
#Add SharePoint PowerShell SnapIn if not already added
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
function SomeFunctionName
{
Param(
[Parameter(Mandatory=$true)]
[string]$CollectionUrl,
[Parameter(Mandatory=$true)]
[string]$SourceList,
[Parameter(Mandatory=$true)]
[string]$DestList,
[Parameter(Mandatory=$true)]
[string]$ExpireDays
) # END PARAMS
#DO SOMETHING WITH THE PARAMETERS
}
If I remove the "function" and surrounding brackets just try to request parameters directly in the script I get the following error:
Missing closing ')' in expression.
You need to put the param(...) block at the top of the script before the If/Add-PSSnapin. You can have comments before the param but no other script.
Related
I'm trying to write my first function and am having some issues. When I run the below I get no output. I feel like I'm missing something obvious but I'm not sure what.
function findModifiedFiles {
[CmdletBinding()]
param (
[string]$dir,
[int]$days
)
Process {
Write-Host "Directory: " $dir
Write-Host "Days: "$days
}
}
Output:
You ultimately need to load your function and then call the function to receive any output. Since your function is defined in a file, one way to load the function is by dot sourcing the file. Then you can simply call your function.
. .\modfilesTest.ps1
findModifiedFiles -dir c:\temp -days 7
An alternative is to not use a function at all just run the script with parameters. If we edit your file to contain the following, we can just call the script afterwards.
# modfilesTest.ps1 Contents
[CmdletBinding()]
param (
[string]$dir,
[int]$days
)
Process {
Write-Host "Directory: " $dir
Write-Host "Days: "$days
}
Now call the script with your parameters.
.\modfilesTest.ps1 -dir c:\temp -days 7
A third alternative is to just paste a function definition into your console. At that point, the function is loaded into your current scope. Then you can just call the function.
I have the below PowerShell script
Function Publish
{
Param(
[parameter(Mandatory=$true)]
[String]
$RELEASEDIR,
[parameter(Mandatory=$true)]
[String]
$SERVICENAME,
[parameter(Mandatory=$true)]
[String]
$SERVER
)
Get-ChildItem "$RELEASEDIR\*"
$service = Get-Service -Name $SERVICENAME -Computername $SERVER -ErrorAction SilentlyContinue
$service.Status
}
Publish
How I am executing this:
PS C:\Release\RPCPS> .\RPCPublish.ps1 -RELEASEDIR "C:\Location" -SERVICENAME "value" -SERVER "server"
cmdlet Publish at command pipeline position 1
Supply values for the following parameters:
RELEASEDIR:
Even after passing arguments while executing, the script is expecting it again. What am I doing wrong here?
If you want to execute the script by calling the .ps1 as in your example, there is no need to use a function. Your script should look just like this:
Param(
[parameter(Mandatory=$true)]
[String]
$RELEASEDIR,
[parameter(Mandatory=$true)]
[String]
$SERVICENAME,
[parameter(Mandatory=$true)]
[String]
$SERVER
)
Get-ChildItem "$RELEASEDIR\*"
$service = Get-Service -Name $SERVICENAME -Computername $SERVER -ErrorAction SilentlyContinue
$service.Status
The parameters are passed directly to the script and can be used there.
If, on the other hand, you want to establish a (reusable) function, remove just the last line from your script, which calls the function without parameters (which is why it asks for the mandatory parameters every time).
If you remove the last line, you can call the script without parameters once. After that you have a new function Publish in your current session, which you can then call with
Publish -RELEASEDIR "C:\Release\Batchfile" -SERVICENAME "AmazonSSMAgent" -SERVER "10.0.1.91"
independent of the script file.
Your script is creating a function, "Publish", (lines 1-17) and then calling it with no parameters (line 18). Since you've defined parameters as mandatory (lines 4, 7, 10), failing to supply the parameters when you call the function (line 18) causes PowerShell to request values for the unsupplied parameters.
Supplying parameters to the script file itself does not help; there is no mechanism for "automagically" passing those parameters to anything within the script (you would have to explicitly code the script for that).
As Matt suggested in the comments, dot-source your script after deleting line 18, and then call your function explicitly, passing the parameters (publish -RELEASEDIR "C:\Release\Batchfile" -SERVICENAME "AmazonSSMAgent" -SERVER "10.0.1.91").
As per my understanding your requirement is to run the function, and you have to compile the scripts also in Jenkins.
You can do something like this:
Let's say your script name is RPCPublish.ps1 and the path is D:\Folder.
And I can see your function name is Publish.
So in your case,
powershell -command "& { D:\folder\RPCPublish.ps1; Publish }"
You can pass the parameters after this in the script block.
I used a PowerShell plugin (PowerShell) and executed the same.
. "C:\Release\RPCPS\RPCPublish.ps1"
FUunctionName -RELEASEDIR "C:\bin\Release" -SERVICENAME "Service" -SERVER "$env:SERVER" -DISPLAYNAME "Services Air" -BINPATH "D:\Build\Project.exe" -DESCRIPTION "This service hosts Air service" -DESTINATION "d$\Build\"
I have a Powershell question.
I am trying to get a value from a function to a variable, by calling a function with a reference to the variable.
For example:
$var = New.Object System.Object;
Example-Function -OutObject ([ref]$var);
Where the Example-Function is defined like this:
function Example-Function
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)]
[ref]
$OutObject
)
$SomeValue = ...
#Write some output
#Do something...
$OutObject.Value = $SomeValue;
}
This is working OK. The $var variable gets it's value from the function ($SomeValue).
But, this is not working when the Example-Function is imported into remote session, for example:
$creds = New-Object System.Management.Automation.PSCredential('user','pass')
$session = New-PSSession -ComputerName 'ExampleComputer' -Credential $creds -Authentication CredSSP
Import-PSSession -Session $session -CommandName 'Example-Function' -AllowClobber
$var = New.Object System.Object;
Example-Function -OutObject ([ref]$var);
This code is throwing the following error: Cannot process argument transformation on parameter 'OutObject'. Reference type is expected in argument.
I am assuming that this is becuase the Example-Function is now running on the other computer ('ExampleComputer'), while ([ref]$var) is referencing the variable in memory of the computer running the scripts (my computer).
The reason I don't want to (cannot) use the return statement way is becuase my function is writing some output, and in Powershell, everything that is outputed from a function is returned.
So, my question is, can I get a value from a function that has a lot of output into the variable, when the function is running in the remote session?
If it cannot be done by using the [ref] parameter, is there another way?
Thanks
Okay lets try again:
Invoke-Command returns whatever is run in the remote pipeline. Which means you can do:
$var = Invoke-Command -session $session -command {Example-Function}
Which saves everything in the $var variable. You can then filter the results and get whatever information you need.
And please remember [ref] just makes everything more complicated than it actually is.
I have a script like this:
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
$Message
)
Write-Host "Hello, $Message!"
But I want to provide a method that allows users to dot source the script. Initially, I was just thinking I would wrap the whole script in a function to allow dot sourcing:
function Hello
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
$Message
)
Write-Host "Hello, $Message!"
}
The problem with this is that if I did this, it would break the script for some people who are using it the old way. Is it possible to return an object with the same functionality as the script, or is there a way to dot source the script as it is (the first example above)?
I would suggest using splatting with $PSBoundParameters:
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
$Message
)
function Hello
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
$Message
)
Write-Host "Hello, $Message!"
}
Hello #PSBoundParameters
Alternatively: instead of dot-sourcing just create function from existing script:
$code = Get-Content .\HelloScript.ps1
New-Item -Path function:\Hello -Value ([scriptblock]::Create($code))
At the end of function Hello you must trigger it to simulate old behaviour. So if the script used to take an argument from command line, lets say c:\scripts\hello.ps1 bob, you can do it as so:
function Hello
{
...
}
if($args.Count -eq 0){
Hello $null
}
else{
Hello $args
}
$args will be an empty array if you were to run c:\scripts\newhello.ps1 without an argument, rather than $null as previously when no value was passed. Therefore we check $args.Count before triggering the function with correct parameter.
I know this is incredibly old but I just ran into it while doing something similar. I have a Foo.ps1 script that needs to work as a script but that I would also like available as one function exported from a module. It seems (I just started playing with it) as if putting the following (in Foo.psm1) function Foo { . "$PSScriptRoot\Foo.ps1" } works like a charm. Avoids having to write out all the arguments in two places.
I have a script that accepts a string parameter :
script-that-takes-string-param.ps1
param(
[Parameter(Mandatory=$true, HelpMessage="path")]
[string]$path,
)
And I have another script that calls the first script :
parent-script.ps1
function CreateDir($dir) {
if (!(Test-Path $dir)) {
mkdir $dir
}
}
function CreatePath($BaseDir, $Environment, $Site, $Domain){
$path = [string]::format("{0}{1}\{2}\{3}", $BaseDir, $Environment, $Site, $Domain)
CreateDir $path
$path
}
$path = CreatePath 'c:\web\' 'qa' 'site1' 'com'
.\script-that-takes-string-param.ps1 -path $path
Running this script throws the exception :
"Cannot process argument transformation on parameter 'path'. Cannot convert value to type System.String"
Casting the parameter doesn't work :
.\script-that-takes-string-param.ps1 -path [string] $path
And casting the function result doesn't work either :
$path = [string] CreatePath 'global' 'site1'
But what is really strange is that if I run parent-script.ps1 twice from the PS command line, the 1st time it throws exceptions, but the 2nd time it executes with no errors.
My best guess would be that your
#do some other stuff with $path
writes something to the standard output, causing the function to return an array that contains said output and the path you expect. Can you send details on what you do in that bit?
Try removing "return". Output that is not saved to a variable is automatically returned. It shouldn't do any difference but it won't hurt to try.
Can you provide a full exception? Without seing the complete exception I get the feeling that the error is caused by something inside your script(ex. a function).
EDIT Your mkdir is causing the problem. When you run it, it returns an object representing the created directory(a DirectoryInfo object if I remember correctly). To fix this, try:
function CreateDir($dir) {
if (!(Test-Path $dir)) {
mkdir $dir | out-null
}
}
or combine them like:
function CreatePath($BaseDir, $Environment, $Site, $Domain){
$path = [string]::format("{0}{1}\{2}\{3}", $BaseDir, $Environment, $Site, $Domain)
if(!(Test-Path $path -PathType Container)) {
New-Item $path -ItemType Directory | Out-Null
}
$path
}