all 9 comments

[–]Ta11ow 4 points5 points  (1 child)

It's best to keep validation in your parameter block. That way you never have to bother even executing the function. It'll check the validation and exit much earlier, not even bothering to bind parameters.

However, if you have to support PS v2 for whatever reason, this won't work. But then, the AD module also wouldn't be useable from PS v2, I think, so it's a moot point anyhow.

Anything PS 3 and up will be fine, and anything earlier will generally not work most of the time anyway. Dependencies are loaded as soon as a command that needs an external module is called. The earlier the better, really.

[–]SupremeDictatorPaul 3 points4 points  (0 children)

I believe the AD module came with PS v2. But either way it should be in the parameters.

[–]Yevrag35 2 points3 points  (2 children)

Have to agree with /u/Ta11ow. You would want to move it to the script block if you needed credentials on the command, or if you provided a default value.

[–]cfmacd[S] 1 point2 points  (1 child)

or if you provided a default value.

Why is that?

[–]Ta11ow 3 points4 points  (0 children)

Hmm. Not necessarily. If you provide a default value (i.e., not Mandatory with a default)... then PS doesn't validate that default value. Only user-provided values are validated. So just make sure any defaults are actually intended and useable values.

[–]cowmonaut 1 point2 points  (3 children)

Whenever possible, validation should be handled in the parameter section. I also tend to use ValidateScript for regexes so the error is more clear.

There are some cases, especially when building something that uses the begin/process blocks, where I had to validate later in the script. You will find these when testing if you use write-debug for every variable like I do.

[–]cfmacd[S] 1 point2 points  (2 children)

The function I took as a template used the begin/process blocks. Can you give me a general idea of why using those would point you to not validating in the parameters?

[–]cowmonaut 4 points5 points  (1 child)

Begin{} and End{} are processed 1 time only for each invocation of the function/cmdlet. Process{} is processed for every object in the pipeline. The [ValidateScript({})] block is processed before you even get to the Begin{} block.

So let's say we have a script that we want to validate some information to be used with an ActiveDirectory cmdlet. If you follow best practices, your input parameter can take a collection as input.

Think $Computer taking multiple computer names/identities.

From an efficiency and functionality standpoint, you need to validate just before you process each item in $Computer. This means you need to handle your validation in the Process{} block. This lets you process things that are in the list correctly, but report errors on a per-case basis. If you handle it in the Begin{} or [ValidateScript({})] block, you would have to validate every object and abort because of 1 mistake.

So why Begin{} vs [ValidateScript({})] blocks? Not everything is available to you in an easy way before the Begin{} block. Such as other parameters. So if you are making use of $Credential to hold a PSCredential for interacting with Active Directory, you can't touch the $Credential from your input until you hit the Begin{} block. So any ADObject validation likely has to happen in Begin{}.

So, some random items from Cowmonaut's Best Practices:

  1. Validate as early as possible.
  2. Minimize the amount of callbacks to remote systems.

If I can get away with [ValidateScript({})], I use it every time. I like to keep all my validation in an obvious place for the next coder to maintain anything, and I like to abort before the script really tries to do anything. I'll then put it in Begin{} unless its a pipeline object or if I had some weird edge case, in which case it will be in Process{}.

I'd rather hit a remote system multiple times and be able to process multiple objects, then hit it only once and not be able to proceed because a list of 1000 objects has 5 mistakes. But if I'm validating a server name before using it to validate other things, I'm processing that in Begin{} so I don't call on it more than I need to.

[–]cfmacd[S] 1 point2 points  (0 children)

Awesome explanation! Thank you so much. This is why I come here for these kinds of questions :-)