Note:
If you are new to PowerShell Parameter Validation you may want to have a look at the official documentation first.
As you probably know, validation attributes are some of the very interesting features of PowerShell.
And, among all parameters a function or script can have, -ComputerName
(or similar) is probably one of the parameters SysAdmins are using the most.
Now, how could you more efficiently validate a piece of code with -ComputerName
as one of its parameters?
This depends on the usage of your function or script and the context:
- The computer does not yet exist and you want to create it
- The computer already exists and is supposed to be available for some distant work
Here are some possible validations methods.
Just take and adapt those you need.
Validation examples
Type validation
Usually, a basic validation looks like this:
param( [ValidateNotNullOrEmpty()] [string]$ComputerName )
You might think that the [string]
validation is useless given that any character combination can constitute a string.
And this is true even with forbidden characters for FQDN names and NetBIOS names.
However, the [string]
validation ensures that the parameter is really a string,
and not by mistake another type of object like a date
, a secure string
, and so on.
It is important because the type of object will determine what you will be able to do later with it in your function or script.
Furthermore, if you use other validation script blocks from the list below, they all require a string as input.
Validating Active Directory computer names
param( [ValidateScript({Get-ADComputer -Identity $PSItem})] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Note: You need the ActiveDirectory module.
Validating NetBIOS compatible computer names
This can be useful when:
- You want to work with standalone computers.
- You want to create a new virtual machine.
param( [ValidateLength(1, 15)] [ValidateScript({$PSItem -replace '\\|/|:|\*|\?|"||\||\.' -eq $PSItem})] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Validating FQDN compatible computer names
This validates only the name and not the full FQDN.
param( [ValidateLength(1, 63)] [ValidatePattern('^[a-z0-9-]+$')] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Validating full FQDNs
param( [ValidateLength(6, 253)] [validatePattern('^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$')] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Validating DNS registered computers names
param( [ValidateScript({Resolve-DnsName -Name $PSItem})] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Validating PowerShell Remoting capable computers
Note
This is the same validation you would use for CIM remoting capable computers,
except if you force the connection to use DCOM instead.
param( [ValidateScript({Test-WSMan -ComputerName $PSItem})] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Validating SSH Remoting capable computers
param( [ValidateScript({(Test-NetConnection -ComputerName $PSItem -Port 22).TcpTestSucceeded})] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Validating SMB capable computers
This can be useful when you want to map a network drive.
param( [ValidateScript({(Test-NetConnection -ComputerName $PSItem -CommonTCPPort 'SMB').TcpTestSucceeded})] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Validating SQL capable computers
In addition to validating the port availability, it also tries a connection with your current credential.
param( [ValidateScript({(Test-DbaConnection $PSItem).ConnectSuccess})] [ValidateScript({(Test-NetConnection -ComputerName $PSItem -Port 1433).TcpTestSucceeded})] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Note: You need the dbatools module.
Validating a whole list of computer names
To accept a list of computer names you probably already know that you only have a little change to make: replace [string]
with [string[]]
.
Of Course, you will have to adapt your main code accordingly to handle your computer list. But this is out of the scope of this article…
However, what about the validation itself?
Fortunately, PowerShell is “intelligent” enough.
You don’t need to bother with a ForEach-Object
cmdlet inside your validation script block.
Each element of your computer name list will be validated separately without changing anything else in your validation code block.
Caveats
Multiple computer names validation
When you use [string[]]
, your function or script will fail when at least one computer name does not pass the validation tests.
For example, this command line will completely fail if Comp2 is invalid. Even Comp1will not be processed.
MyScript.ps1 -ComputerName 'Comp1','Comp2'
However, if you want to work with valid computer names only, while skipping invalid computer names, you can use the pipeline directly or indirectly with the ForEach-Object
cmdlet.
Directly with the pipeline
- implement the
ValueFromPipeline
attribute - implement a
process
script block - in the command line, use the pipeline to execute the function or the script
For example, if we adapt the registered DNS computer names validation example of above,
the code which includes the two first steps could look like this:
param( [Parameter(ValueFromPipeline)] [ValidateScript({Resolve-DnsName -Name $PSItem})] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process{ #This block will be executed once for each valid computer name $ComputerName }
And for the third step the command line could look like this:
'Comp1','Comp2' | MyScript.ps1
Note: More about the process
script block here.
indirectly with the ForEach-Object
cmdlet
Alternatively, if you don’t want to change the code of your function or script, you can just use the ForEach-Object
cmdlet to run your function or script several times with one computer name at a time.
'Comp1','Comp2' | ForEach-Object -Process {MyScript.ps1 -ComputerName $PSItem}
Required modules
Depending on the audience of your function or script, you may want to give a hint when a required module for a validation script attribute is missing.
However, and unfortunately, the #Requires
statement is processed after all parameters are validated.
And inside a validation script block, the #Requires
statement is ignored.
But, as a workaround, you can make a check on your own and throw a terminating error.
The second example from the beginning (validating Active Directory computers) could look like:
param( [ValidateScript( { if (-not(Get-Module -Name 'ActiveDirectory' -ListAvailable)) { throw 'The ActiveDirectory module is missing on this computer!' } else { Get-ADComputer -Identity $PSItem } })] [ValidateNotNullOrEmpty()] [string]$ComputerName )
Formerly you would have gotten this error message:
And now you get this error message, which leads PowerShell beginners to the solution:
Combining multiple validations
You can combine multiple validations.
For example, you may want that a computer name is valid as a NetBIOS name and also as a DNS name at the same time.
Just remember that they are processed reversely compared to the reading order (starting from the variable name and going back)
In the last example above (SQL compatible computers), the effective validation order is:
- The value is a string object
- The value is not null nor empty
- The computer name is reachable over TCP port 1433
- A SQL connection with credential is attempted against the computer name
Another option is: [System.Uri]::CheckHostName(‘computer’)
A valid computer name will return Dns, but an invalid computer name will return Unknown.
LikeLike
Yes it validates a whole FQDN.
You can use the ValidateScript attribute with your piece of code.
For example:
[ ValidateScript ( { if ( [System.Uri]::CheckHostName ($PSItem) -eq ‘Dns’ ) { $true } } ) ]
LikeLike