sirbounty
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
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 {
}
}
Is/are your letters validation case sensitive?
ASKER
I don't believe so - I'm essentially using a get-aduser against the return.
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.
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
Test for allowed domains:$allowedDomains = 'abc.com', 'def.org'
$user, $domain = $Matches['Email'].Split('@')
If ($allowedDomains -contains $domain) {
# ...
}
I was going to suggest using a switch statement
switch -regex ($UserID) {
}
until I saw that you were doing this: $Prefix = $Matches['Prefix']
ASKER
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.
route.
I just need to be able to send the result to a get-aduser cmd.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
oBdA
You can stop comparing when you get a match in your switch statement.
You can stop comparing when you get a match in your switch statement.
ASKER
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?
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?
ASKER
Odd because if I run $userID -match against the Email version, it returns True. :\
EXPERT CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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.
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.
ASKER
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?
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.
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.
ASKER
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?
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"):
Note, too, that if you're allowing wildcards, more than one account might be returned.
"userPrincipalName -eq '$($UserID)'"
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.
ASKER
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?
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?
ASKER
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'])')"
ASKER
Ah - I like that.
Any idea on the last names with an apostrophe? The switch doesn't catch it unfortunately.
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 `):
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" {
ASKER
Yes, <sigh>, we have a John.O'Brian... :\
I'll make that adjustment and get back to you shortly - thanks!
I'll make that adjustment and get back to you shortly - thanks!
ASKER
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.
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.
ASKER
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.
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:
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'])')"
ASKER
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!
ASKER
Can't thank you enough - fully tested and fully working. :)