# Validate Anything

Typecasts can be used to easily validate complex input such as IP addresses or dates. You may want to compliment this with simple pattern checks.

Type casts can be an extremely powerful strategy to validate complex data formats. This approach may be much easier than using Regular Expressions.

## Operator -as To The Rescue

The operator -as is PowerShells equivalent to a TryCast: the operator either successfully converts data to the desired target type, or it silently returns $null. If a cast is not successful, it won’t throw an exception, however. ### Testing Dates Let’s assume you need to validate whether a user entered a valid date. Since there are many valid date and time formats, using Regular Expressions can be extremely complex. By trying to cast the user input to [DateTime], you leave the hard part to PowerShell. Test-Date tests any raw user input and returns $true when the user input can be converted to [DateTime].

The sample below keeps prompting the user until a valid date is entered:

function Test-Date($UserInput) { return$UserInput -as [DateTime] -ne $null } do {$value = Read-Host -Prompt 'Enter your birthday'
} until (Test-Date -UserInput $value) "$value is a valid Date."

$timespan = New-Timespan -Start ($value -as [DateTime])
$days =$timespan.TotalDays
"You are {0:n0} days old." -f $days  The date is then used to calculate the time difference (in days), so if the user entered his birthday, the age in days is returned. You may be wondering why the code uses the operator -as twice. This is because I wanted to illustrate a generic test function, and Test-Date returns a boolean value, telling you whether the entered value was a valid date or not. It does not change the user input. So if you want to actually interpret the user input as date, you again have to convert it, and this time use the converted result. If you did not use -as again and instead submitted the raw user input, PowerShell would use explicit casting. With dates, it would use the culture-neutral conversion (locale en-US) which could yield wrong results if you are using a different locale. Obviously you could have simplified the code and use -as only once at the expense of a less reusable approach: do {$value = (Read-Host -Prompt 'Enter your birthday') -as [DateTime]
} until ($value)$timespan = New-Timespan -Start $value$days = $timespan.TotalDays "You are {0:n0} days old." -f$days


As long as you can find a type that adequately represents the format you are after, you are fine. If you wanted to check for IPv4 IP addresses, you might be intrigued to use the type [System.Net.IPAddress]:

function Test-IPv4Address($IPAddress) {$IPAddress -as [System.Net.IPAddress] -ne $null }  At first sight, this seems to work: PS> Test-IPv4Address -IPAddress 10.10.10.10 True PS> Test-IPv4Address -IPAddress hello False PS> Test-IPv4Address -IPAddress 10.12.3.500 False  However, with extensive testing you’ll soon discover flaws: PS> Test-IPv4Address -IPAddress 10.12 True PS> Test-IPv4Address -IPAddress 0 True  ### You Must Understand The Conversion If you trust in automatic conversion, you must understand the conversion rules or else you’ll run into situations where the conversion succeeds even though you had expected it to fail. How come the input 10.12 successfully converts to [System.Net.IPAddress]? Find out! [System.Net.IPAddress]10.12  The result reveals that the internal conversion algorithm happily converts this to the IP address 10.0.0.12: Address : 201326602 AddressFamily : InterNetwork ScopeId : IsIPv6Multicast : False IsIPv6LinkLocal : False IsIPv6SiteLocal : False IsIPv6Teredo : False IsIPv4MappedToIPv6 : False IPAddressToString : 10.0.0.12  ### Fine-Tune Conversion By combining type conversion with a simple pattern check, in most circumstances you can create powerful and highly specific results. To check for IPv4 addresses, simply check for the typical IPv4 pattern as well: function Test-IPv4Address($IPAddress)
{
$IPAddress -like '*.*.*.*' -and$IPAddress -as [System.Net.IPAddress] -ne $null }  Now Test-IPv4Address is returning $true only if the user input was convertible to [DateTime] and matched the simple pattern.

PS> Test-IPv4Address -IPAddress 10.12.1.100
True

False

False

False


Generally, the hardest part of type conversions to validate information is to find a suitable data type in the first place that adequately represents the type of information. With a little investigation effort, you’ll find types for almost any IT-related data type, though.

Here is a function that tests valid email addresses:

function Test-EmailAddress($EmailAddress) {$EmailAddress -as [System.Net.Mail.MailAddress] -ne $null }  Again, make sure you test-drive the results and understand the conversion algorithm: PS> Test-EmailAddress -EmailAddress [email protected] True PS> Test-EmailAddress -EmailAddress tobias False PS> Test-EmailAddress -EmailAddress [email protected] True  Apparently, for the conversion it is ok for the host name to have no extension, and domains like abc are in order. If you need stricter rules, again add simple pattern checks: function Test-EmailAddress($EmailAddress)
{
$EmailAddress -like '?*@?*.??*' -and$EmailAddress -as [System.Net.Mail.MailAddress] -ne $null }  Simple patterns are simple because they primarily use only two wildcards: * representing anything (including nothing), and ? representing exactly one character. By cleverly combining both, you can ensure that a minimum number of characters are required. For example, in Test-EmailAddress, there need to be at least one character before and after the @ sign, and the domain part must be at least two characters. The result is now sufficient for most purposes: PS> Test-EmailAddress -EmailAddress [email protected] True PS> Test-EmailAddress -EmailAddress [email protected] True PS> Test-EmailAddress -EmailAddress [email protected] False PS> Test-EmailAddress -EmailAddress '@powershell.one' False  ## Testing Numerics Testing for numeric input can be tricky, but using conversion, it is a snap: function Test-Numeric($Value)
{
$value -as [int64] -ne$null
}


Let’s test-drive:

PS> Test-Numeric hello
False

PS> Test-Numeric 12
True

PS> Test-Numeric 12.798578
True

PS> Test-Numeric +66
True

PS> Test-Numeric -8276
True

PS> Test-Numeric 0xab6f
True

PS> Test-Numeric 8.23MB
True


Try this with Regular Expressions, and you’ll see how hard it is to include all the special edge cases where users enter hex numbers or use units like MB.