1

I have a script with mandatory parameters which we use to install some SQL components including user name and passwords like below:

param(    
[Parameter(Mandatory=$True,HelpMessage="SQL Server password")]
    [ValidateNotNullOrEmpty()]
    [string] $SqlServerPassword
)

So when a user runs this script, he/she will need to include the -SqlServerPassword 'SpecialCharacters' variable string. I know best practice is to place the string inside a single quote, but its been a hard path training some of our installation managers and it messes up because our password vault includes special characters which without single quotes causes issues.

How can I re-write the above to ensure that even if the user passes the password without it being in single quotes, that it will be in single quotes? Thanks!

8
  • Well, for a start if you are supporting all those versions of powershell in your environment you should look at standardization. Commented Jun 12, 2018 at 19:07
  • Also, could you expand on "..because our password vault includes special characters which without single quotes causes issues."? I don't understand what you mean. Commented Jun 12, 2018 at 19:08
  • @EBGreen I get standardization and agree. But for right now, until we re-architect some things, I have to deal with this which is a constant pain because people forget. Its just a random password generator. Commented Jun 12, 2018 at 19:11
  • @EBGreen apologies, I misunderstood - we are using PS version 5 across the enterprise. Commented Jun 12, 2018 at 19:13
  • 1
    Aah...Well if I get a chance I will play around with this. I can reproduce it. Here is a minimal reproducible example for anyone that wants one: function foo{param($in) Write-Host $in}; foo abcd$123 I doubt that there is a solution. You can't ever completely out-program stupid. Commented Jun 12, 2018 at 19:21

1 Answer 1

1

What you're asking for cannot be done, if the string is to be passed as an argument, because that would require deactivating the command-line parser - the very mechanism that recognizes individual arguments, evaluates variable references and subexpressions contained in them, and binds them to parameters.

With a limited set of special characters you could ignore the value bound by the parser and manually parse $MyInvocation.Line, the raw command line, but not only is that ill-advised, it would break with characters such as | and ;

However, you can achieve what you want via an interactive prompt. While you also get such a prompt with your code if the user happens not to pass a -SqlServerPassword argument on the command line, it doesn't prevent potentially incorrect command-line use. (Also, this automatic prompting is not user-friendly and has quirks, such as not being able to start a value with !).

If feasible, you could require users to enter the value interactively (and not also allow passing a value as an argument) using Read-Host, in which case quoting need not - and must not - be used:

param(
  # Do NOT define $SqlServerPassword as a parameter (but define others)
)

# Prompt the user for the password.
# The input is invariably treated as a literal.
Write-Host "Please enter the SQL Server password:"
[string] $SqlServerPassword = Read-Host

# Validate the user's input.
if (-not $SqlServerPassword) { Throw 'Aborted.' }
# ...

Note: Read-Host has a -Prompt parameter, which accepts a prompt string directly, but if you were to use it, you wouldn't be able to enter values that start with !; therefore, the prompt was written with a separate Write-Host statement above.

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

4 Comments

I tested this and it does accept the password with special characters; although, if a password starts with ! it could be a potential issue. I am curious whether this should go inside the param() or outside of it? So in the script, there are various parameters, the SQL password is just one, there is also the user, and other passwords. I can use the prompt, which I see does work but do I place inside the param() or outside of it? Like right underneath. So I have my param() commenting out the password ones and place the [string] $SqlServerPassword = Read-Host outside of that? Thanks!
@DangeRuss: Yes, the prompt should either go into the main body of your function - right after the param() block - or, if it is an advanced function with begin / process / end blocks, in the begin block. Another option is to stick with the original parameter but make it non-mandatory and then, if an argument was passed, show its value and ask the user to confirm / revise it - that way, they'll see if what they meant to pass wasn't passed due to lack of quoting. If no argument was passed, then use the prompt as shown above.
@DangeRuss: A password starting with ! is not a problem in the solution in the answer, because it avoids both automatic prompting and use of Read-Host's -Prompt parameter.
Thank you! I tested this yesterday and prompting works and keeps the special characters no matter what they are. :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.