1

I'm pretty new to PowerShell but loving it for automating loads of tasks on our Windows machines. I love that you can call functions from other scripts, however the scripts I have written all use parameters the user can provide (so it's easier for colleagues to use them).

There is one parameter in particular that is usually mandatory in my scripts. The problem I'm facing is calling functions from scripts with mandatory parameters.

Here's a simple example:

Param(

 [Parameter()]
 [ValidateNotNullOrEmpty()]
 [string]$VirtualMachine=$(throw "Machine name missing!"),

 [int]$Attempts = 150

 )

Function DoSomething($VirtualMachine, $Attempts){

    write("$VirtualMachine and $Attempts")

 }

Running this as a script you would provide -VirtualMachine "VMnameHere" -Attempts 123. Running this would produce VMnameHere and 123. Perfect! However.. If I try to call this as a function from another script..

Example here:

. ".\Manage-Machine.ps1"

DoSomething -VirtualMachine "nwb-thisisamachine" -Attempts 500

This produced an error:

Machine name missing!
At C:\Users\something\Desktop\Dump\play\Manage-Machine.ps1:33 char:28
+  [string]$VirtualMachine=$(throw "Machine name missing!"),
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Machine name missing!:String) [], RuntimeException
    + FullyQualifiedErrorId : Machine name missing!

Which is clearly because the field is mandatory. Am I doing something wrong in how I'm calling the function in this case? Is there an alternative way to calling the function if the script it belongs to has mandatory parameters, because if I remove the validation for the parameter, it all works.

Would love some input,

Thank you!

9
  • 1
    Does the ps1 file have a line that calls the function? Because your sample should not generate output. If it does then just call the ps1 and provide your arguments Commented Nov 24, 2017 at 16:37
  • I would use [parameter(Mandatory = $true)] and remove =$(throw "Machine name missing!") Commented Nov 24, 2017 at 16:40
  • @Matt No it doesn't, at the moment, if I call the function (as described without the mandatory parameters) it runs just fine. Running the script would work fine, but what I'm really after is having multiple functions in a single script and then calling each function when I need it in another script, rather than running the whole script. Which would run any function called at the end, I wouldn't be able to use a function at a time. Commented Nov 24, 2017 at 16:42
  • 1
    What James is saying would still throw an error. No need to manually handle no input case is what he is suggesting. Throw can make for better messages though Commented Nov 24, 2017 at 16:44
  • 1
    @JakubS You'd said 'parameters the user can provide' but if instead it's part of a CI process then run powershell with the -NonInteractive flag and any missing mandatory parameters will cause an error and a non-zero exit code will be returned. Commented Nov 24, 2017 at 16:49

2 Answers 2

2

I would use [parameter(Mandatory = $true)] and remove =$(throw "Machine name missing!").

You can then run powershell with the -NonInteractive flag (documentation link) and any missing mandatory parameters will cause an error and a non-zero exit code will be returned.

This return code should be picked up by your CI process and it itself will handle the error.

Sign up to request clarification or add additional context in comments.

2 Comments

What you're suggesting is unrelated to the OP's problem. Your suggestion would change only one thing: in interactive invocation scenarios, [Parameter(Mandatory)] would prompt in the absence of mandatory parameter values instead of throwing an exception (via $(throw "Machine name missing!")).
This is an old post, but the -NonInteractive flag solved my issue.
1

I'm not sure it's such a great idea to do this, but it sounds like the following would work:

Param(

  [ValidateNotNullOrEmpty()]
  # Do NOT use = $(Throw ...) or [Parameter(Mandatory)].
  [string]$VirtualMachine, 

  [int]$Attempts = 150

)

# Determine if the script is being "dot-sourced".
# Note: The `$MyInvocation.Line -eq ''` part detects being run from the
#       ISE or Visual Studio Code, which implicitly perform sourcing too.
$isDotSourced = $MyInvocation.InvocationName -eq '.' -or $MyInvocation.Line -eq ''

# NOT sourced? Enforce mandatory parameters.
if (-not $isDotSourced) {
  if (-not $VirtualMachine) { Throw "Machine name missing!" }
}

Function DoSomething($VirtualMachine, $Attempts) {
  "$VirtualMachine and $Attempts"
}


# NOT sourced? Call the default function or
# do whatever you want the script to do when invoked as a whole.
if (-not $isDotSourced) {
  DoSomething $VirtualMachine $Attempts
}
  • . .\Manage-Machine.ps1 will then merely define the functions (DoSomething in this case), for later invocation;
    since none of the script parameters are technically declared as mandatory, invocation without parameters will succeed (unlike in your attempt, where the throw statement invariably kicked in - whether directly invoked or dot-sourced).

  • .\Manage-Machine.ps1, by contrast, will enforce the presence of a $VirtualMachine parameter value and instantly call DoSomething, passing the parameter values through.

Note that, of course, your functions could benefit from typing your parameters and adding validation attributes, too.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.