1

I'm trying to create a powershell function that validates users names and if a tech inputs the wrong character, I want it to throw an error message as to why it was wrong and restart the script for them to make the choice again.

So far I have this

Running VSCode with Powershell Extension 2022.8.5

function stringTest {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateLength(4,15)]
        [ValidatePattern('^[a-zA-Z0-9-_]+$')]
        [string] $alphaTest 
        
    )
        Write-Host $alphaTest
}

$writeHere = Read-Host "UserName: "

stringTest($writeHere)

Output:

UserName: doej

doej

This works fine, but I want to try and add Custom error messages using the ErrorMessage within Validate Pattern. So I would try this

function stringTest {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateLength(4,15)]
        [ValidatePattern({$pattern = "^[a-zA-Z0-9-_]+$([Regex]::escape($pattern))\s"
        if ($_ -in $pattern ) {return $true}
        throw "$_ is not a valid character. Valid characters are: '$($pattern -join ',')'"
    })]
        [string] $alphaTest 
        
    )
        Write-Host $alphaTest
}

$writeHere = Read-Host "UserName: "

stringTest($writeHere)

But now my Validate doesn't actually validate anymore? I try the same name or anything different that "should" be valid

Cannot validate argument on parameter 'alphaTest'. The argument "doej" does not match the "$pattern = | "^[a-zA-Z0-9-]+$([Regex]::escape($pattern))\s" if ($ -in $pattern ) {return $true} throw "$_ is not a valid | character. Valid characters are: '$($pattern -join ',')'" " pattern. Supply an argument that matches "$pattern = | "^[a-zA-Z0-9-]+$([Regex]::escape($pattern))\s" if ($ -in $pattern ) {return $true} throw "$_ is not a valid | character. Valid characters are: '$($pattern -join ',')'" " and try the command again.

From the looks of it, it's trying to match the regex pattern exactly instead of working the way before. Any help would be greatly appreciated or pointing me in the write direction for this.

6
  • Both answers here should cover the approaches you can take, either a custom class or ValidateScript Commented Sep 20, 2022 at 22:50
  • You're passing a [ScriptBlock] to ValidatePattern, which, of course, expects a (string) pattern. Use ValidateScript instead. Also, you'd want to use $_ -match $pattern, not -in. Commented Sep 20, 2022 at 23:08
  • 1
    @SantiagoSquarzon That class method is going to come in very handy for when I take this and try and make a multi-tool package of sorts for my team. I tried searching up the terms for parameters but apparently I didn't search "just" outside my scope enough as you provided a plethora of sources! Thank you for that! Commented Sep 21, 2022 at 10:14
  • @LanceU.Matthews Ahhhh... it was specifically looking for the characters in the pattern and not trying to >match< the regular expression! Thank you! That validatescript worked as well! Commented Sep 21, 2022 at 10:14
  • I just wanted to add. The ErrorMessage call only works with PS core 6+ Commented Sep 22, 2022 at 0:40

1 Answer 1

0

You can have your custom attribute declaration using a class as described in this answer, both ValidatePattern and ValidateLength attribute declarations inherit from the same base class, ValidateArgumentsAttribute Class.

You can also use ValidateScript as described in mklement0's answer.

Here is one example of how the custom class would look like:

using namespace System.Management.Automation

class ValidateCustomAttribute : ValidateEnumeratedArgumentsAttribute {
    hidden [int] $Min
    hidden [int] $Max
    hidden [string] $Pattern

    ValidateCustomAttribute() { }
    ValidateCustomAttribute([scriptblock] $Validation) {
        $this.Min, $this.Max, $this.Pattern = & $Validation
    }

    [void] ValidateElement([object] $Element) {
        if($Element.Length -lt $this.Min -or $Element.Length -gt $this.Max) {
            throw [MetadataException]::new(
                [string]::Format(
                    'Invalid Length! Must be between {0} and {1}.',
                    $this.Min, $this.Max
                )
            )
        }
        if($Element -notmatch $this.Pattern) {
            throw [MetadataException]::new(
                [string]::Format(
                    'Invalid Argument! Does not match {0}.',
                    $this.Pattern
                )
            )
        }
    }
}

As for the implementation:

function Test-Attribute {
    param(
        [ValidateCustomAttribute({ 2, 10, '^[a-zA-Z0-9-_]+$' })]
        [object] $Test
    )

    $Test
}

This syntax is also valid for the attribute declaration:

[ValidateCustomAttribute(Min = 2, Max = 10, Pattern = '^[a-zA-Z0-9-_]+$')]

And the testing:

PS /> Test-Attribute 'hello???'
Cannot validate argument on parameter 'Test'. Invalid Argument! Does not match ^[a-zA-Z0-9-_]+$.

PS /> Test-Attribute 'somelongstring'
Cannot validate argument on parameter 'Test'. Invalid Length! Must be between 2 and 10.

PS /> Test-Attribute hello
hello

As aside, the pattern can be reduced to just ^[\w-]+$.

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

4 Comments

Thank you so much again for these Santiago! I mentioned before these would come very much in handy when I start scripting full on modules specific for my team and will be taking a shot at these. On the aside that regex does work almost perfectly but for some reason it still accepts characters i try to exclude... I tried excluding !@#$%^&*()=+ specifically but if I try and validate sm!ith or smith! or smith=t2 Powershell still prints the statement as valid. I've tried ^[\w-_[^(?:!@#$%^&*()+=)]]+$ and in regexer it shows it shouldn't capture the string of text but PWSH does?
Hi @RonaldPusey I tested this again and works fine for me: i.imgur.com/2v8ifsc.png are you sure the class has the condition if($Element -notmatch $this.Pattern) { ... } ?
I think you have a misunderstanding of how it works, you need a regex for the characters you want to allow, NOT the ones you want to exclude. So ^[\w-]+$ would only allow one or more of a to z or A to Z or 0 to 9 or _ or - only. @RonaldPusey
I tried the class out and it works just as you said! I was still being a goober and testing it from just the function itself apologies. You're amazing!

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.