Link to home
Start Free TrialLog in
Avatar of sirbounty
sirbountyFlag for United States of America

asked on

Function adjustment (regex?)

I've had assistance with this function in the past, but I need to address a bit more criteria now.
I need to ensure that the passed parameter is either
a) A 6-character string, with the first character being a letter among (a,n,s,x) and the remaining 5 being numeric
b) All 7 being numeric
c) a valid email format - and from valid domains.  I have an array $validdomains to compare that against


function Format-UserID { 
    param ([string]$UserID)
    try {
            $uid = $UserID.Substring(0,7).trim()
            if ($uid.Length -eq 6) {
                If ($uid -match "\A(?<Prefix>[ANSX]?)(?<ID>\d{6}).*\Z") {  
                    $prefix = 'A' 
                } else { 
                    $Prefix = $Matches['Prefix']
                }
                return "$Prefix$($Matches['ID'])"
            } else {
                if ($uid.Length -eq 7) {
                    try {
                        if (get-aduser $uid -ErrorAction stop) {
                            return $uid
                        }
                    } catch {
                        'User ID {0} is not a valid ID!' -f $uid| Log-Verbosely 
                        return 'error'
                    }
                } else {
                    'User ID {0} is not a valid ID!' -f $uid| Log-Verbosely
                    return 'error'
                }
            }
    } catch {
    }
}

Open in new window

Avatar of aikimark
aikimark
Flag of United States of America image

Is/are your letters validation case sensitive?
Avatar of sirbounty

ASKER

I don't believe so - I'm essentially using a get-aduser against the return.
Avatar of oBdA
oBdA

Depends mainly when and how you want to test.
You can test all possible input formats in a single RegEx, and in case of a match check which of the named groups is set, or you can do one dedicated RegEx for each case.
Either way, I'd test the allowed email domains outside the RegEx.
# Prefix ANSX followed by exactly 5 digits:
\A(?<PrefixAndNumeric>[ANSX]\d{5})\Z"

# Exactly 7 digits:
\A(?<AllNumeric>\d{7})\Z

# Email (from W3C, https://www.w3.org/TR/2012/WD-html-markup-20120320/input.email.html)
\A(?<Email>[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*)\Z

# All combined:
\A((?<PrefixAndNumeric>[ANSX]\d{5})|(?<AllNumeric>\d{7})|(?<Email>[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*))\Z

Open in new window

Test for allowed domains:
$allowedDomains = 'abc.com', 'def.org'
$user, $domain = $Matches['Email'].Split('@')
If ($allowedDomains -contains $domain) {
	# ...
}

Open in new window

I was going to suggest using a switch statement
switch -regex ($UserID) {
    
}

Open in new window

until I saw that you were doing this: $Prefix = $Matches['Prefix']
I don't believe I have to stick with the $Prefix = Matches['Prefix']
route.
I just need to be able to send the result to a get-aduser cmd.
ASKER CERTIFIED SOLUTION
Avatar of oBdA
oBdA

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
oBdA

You can stop comparing when you get a match in your switch statement.
That's not working.
I had to enclose the last check in double-quotes and enclose all switch blocks inside  { }, but even so, sending my first email address to it drops out at the AllNumeric test... Not sure why?
Odd because if I run $userID -match against the Email version, it returns True. :\
EXPERT CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Could be - I'll do some more testing and let you know.  Thanks
Others have given good suggestions.  I just wanted to add that the email regex is definitely not complete (it definitely allows invalid addresses and likely disallows valid ones) - fully checking email addresses in notoriously hard in a regex.
wilcoxon,
Please see the link (inside the code block) in my first comment. That RegEx is straight from the W3C's definition of a valid HTML email input field, so it's probably close enough to the truth (it doesn't allow for quoted strings, I give you that).
HTML: The Markup Language > input type=email – e-mail address input control
https://www.w3.org/TR/2012/WD-html-markup-20120320/input.email.html
False positives don't matter anyway, since a positive match is only used to create the appropriate filter for the AD query, and I think you'd be hard pressed to find a false negative that is actually used.
Update - I think it's going to work..  Although I'll have to modify the filter.  The email wouldn't be their UPN - it would just be their emailaddress attribute in the cases where they user ID didn't match as their samacccountname.  So I'm using $filter = "emailaddress -like $userID".  Though I'm not certain that's correct if emailaddress can be multi-valued?  Should I use contains?

Any need to place the break on each switch element?
Then just replace  "userPrincipalName" in line 19 with "mail".
I don't see a real need for the "Break" here (which is why I left it out); the matches are all mutually exclusive, and it's only three of them. The user lookup in AD will take longer.
You can add it if you want to, but in this case, it's more a matter of personal preference.
Have a bit of difficulty with the mail filter...
get-aduser: Error parsing query: '$filter' Error message: 'syntax error' at position: '1'.

This works if I type it from the cmd line:
get-aduser -Filter {mail -like $UserID}

This throws an error (no parameter found like 'like'
get-aduser -Filter mail -like $UserID

So I tried including the {} and it still failed.

Tried using this route:
$filter = '{mail -like {0}' -f $userid

But that also failed.  Any ideas?
Like the filter I used above (but you really only had to replace "userPrincipalName" with "mail"):
"userPrincipalName -eq '$($UserID)'"

Open in new window

Note that "-like" without wildcards is the same as "-eq", it only takes longer. If you're using a syntax check to allow only certain formats, you shouldn't circumvent that yourself by allowing wildcards.
Note, too, that if you're allowing wildcards, more than one account might be returned.
One last snafu... it seems that mail attribute works for most.  But I've come across at least one where the mail attribute doesn't match what was passed, however targetaddress does.
Would that be best in the else block under If ($filter) before resolving to it not being found (and altering the filter before searching again), or can you think of a better approach?
Bah - and now I have an O'Brian, and it fails...guess I need a replace function there...
Untested, but nothing keeps you from combining comparisons:
$filter = "(userPrincipalName -eq '$($Matches['Email'])') -or (targetAddress -eq '$($Matches['Email'])')"

Open in new window

Ah - I like that.
Any idea on the last names with an apostrophe?  The switch doesn't catch it unfortunately.
How do you get an apostrophe in there? Do you really have email addresses like John.O'Brian@domain.com?
OK, looking at the RFC, the first of the two "ticks" seems to be an actual single quote (ASCII 39), while said character from the W3C string was turned into a forward tick when I pasted it into Notepad++.
So line 16 should look like this (note the double quotes around the string, and the additional two backticks in front of the $ and the `):
		"\A(?<Email>[a-zA-Z0-9.!#`$%&'*+/=?^_``{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*)\Z" {

Open in new window

Yes, <sigh>, we have a John.O'Brian... :\
I'll make that adjustment and get back to you shortly - thanks!
Eh - test environment is down... I'll try it out again when it comes back online.  But I think this should sort it all - thanks for the help!
oBdA,
Yes, I saw the link.  It may be from W3C but it is not compliant with the RFCs (allowing some illegal email addresses and disallowing some legal email addresses).  It is probably "good enough" but it is definitely not fully compliant/complete.
Update today - adding the filter with the -or, throws a syntax error.
I manipulated it to
$filter = "{mail -eq '$($UserID)' -or targetAddress -eq '$($UserID)'}"

I can, however, take that generated string and paste it into the cmdlet at the prompt and it works.  It's like it doesn't like the variable $filter.
Sorry, but It doesn't really help if you tell me that you changed the filter, and then only post another change that didn't work, either.
What is the exact string you set the filter to, and what is the exact error message you got?
This should work just fine as line 19 above:
				$filter = "(mail -eq '$($Matches['Email'])') -or (targetAddress -like '*$($Matches['Email'])')"

Open in new window

Apologies - you'd said "untested" so I attempted a couple of different routes landing on that one because it worked from the command line.  That latest version seems to do the trick.  Thanx much!
Can't thank you enough - fully tested and fully working. :)