Line break issue when configuring "send on behalf of" - powershell

I have a script to set send on behalf of permissions in Exchange Management Shell, but when you try and use it it fails because the output of the first part is too long and truncates over 2 lines.
First thing we do is build our array from lists of people and put them into some variables to pass:
function Add-Send ($mailbox, $target) {
#"Granting send on behalf for $mailbox to $target"
Set-Mailbox -Identity $mailbox -GrantSendOnBehalfTo #{ Add = $target }
}
We pass a long list as the $target and the maibox name is $mailbox and if we output the text we get:
Set-Mailbox -Identity "mr.jeff" -GrantSendOnBehalfTo #{ Add = "alan.alanson", "bob.bobson", "steve.stevenson" }
All fine and good but if there are more than N characters in the output then we get a line break:
Set-Mailbox -Identity "mr.jeff" -GrantSendOnBehalfTo #{ Add = "alan.alanson", "bob.bobson", "steve.stevenson", ...
..., "cath.cathdotir" }
When you run this script with the overlength output, then command fails as the output which should be passed to the CLI is passed over more than one line. PowerShell treats each line as a separate command, and they obviously fail with bad syntax.
Our string is output from an array that we build like this:
function Send-Array ($mailbox) {
$target = Get-Content ".\list\dpt1.txt"
$target += Get-Content ".\list\$mailbox.txt"
$target += Get-Content ".\list\dpt2.txt"
$target = $target | Select-Object -Unique
$separator = '", "'
$target= $target -replace '^|$','"' -join ','
Add-Send $mailbox $target
}
This gives us an array with strings that look like:
"alan.alanson", "bob.bobson", "steve.stevenson"
From here I am at a loss any ideas would be much appreciated.
The obvious solution would be to pass the names one at a time, but due to a gotcha with Exchange Server every time you set send on behalf of permissions with PowerShell it wipes the existing permissions, so you only end up with he last person granted permissions being able to send on behalf of.

See this link for help with your underlying issue.
Very basically, you will have to:
get the DistinguishedName of the user you need to add
store the current value of GrantSendOnBehalfTo in a variable
append the new user's distinguished name to the list
replace GrantSendOnBehalfTo with the new list
Afterwards you should not need to pass endless strings to the EMS (I hope so).

Related

Get-GPOReport and Search For Matched Name Value

I'm trying to use the PowerShell command 'Get-GPOReport' to get GPO information in XML string format so I can search it for sub-Element values with unknown and different Element tag names (I don't think XML Object format will work for me, so I didn't perform a cast with "[xml]"), but I haven't been able to parse the XML output so that I can grab the line or two after a desired "Name" Element line that matches the text I'm searching for.
After, I have been trying to use 'Select-String' or 'Select-XML' with XPath (formatting is unclear and I don't know if I can use a format for various policy record locations) to match text and grab a value, but I haven't had any luck.
Also, if anyone know how to search for GPMC GUI names (i.e. "Enforce password history") instead of needing to first locate back-end equivalent names to search for (i.e. "PasswordHistorySize"), that would also be more helpful.
The following initial code is the part that works:
$String = "PasswordHistorySize" # This is an example string, as I will search for various strings eventually from a file, but I'm not sure if I could search for equivalent Group Policy GUI text "Enforce password history", if anyone knows how to do that.
$CurrentGPOReport = Get-GPOReport -Guid $GPO.Id -ReportType Xml -Domain $Domain -Server $NearestDC
If ($CurrentGPOReport -match $String)
{
Write-Host "Policy Found: ""$($String)""" -Foregroundcolor Green
#
#
# The following code is what I've tried to use to get value data, without any luck:
#
$ValueLine1 = $($CurrentGPOReport | Select-String -Pattern $String -Context 0,2)
$Value = $($Pattern = ">(.*?)</" ; [regex]::match($ValueLine1, $Pattern).Groups[1].Value)
}
I've been looking at this since yesterday and didn't understand why Select-String wasn't working, and I figured it out today... The report is stored as a multi-line string, rather than an array of strings. You could do a -match against it for the value, but Select-String doesn't like the multi-line formatting it seems. If you -split '[\r\n]+' on it you can get Select-String to find your string.
If you want to use RegEx to just snipe the setting value you can do it with a multi-line regex search like this:
$String = "PasswordHistorySize" # This is an example string, as I will search for various strings eventually from a file, but I'm not sure if I could search for equivalent Group Policy GUI text "Enforce password history", if anyone knows how to do that.
$CurrentGPOReport = Get-GPOReport -Guid $GPO.Id -ReportType Xml -Domain $Domain -Server $NearestDC
$RegEx = '(?s)' + [RegEx]::Escape($String) + '.+?Setting.*?>(.*?)<'
If($CurrentGPOReport -match $RegEx)
{
Write-Host "Policy Found: ""$String""" -Foregroundcolor Green
$Value = $Matches[1]
}
I'm not sure how to match the GPMC name, sorry about that, but this should get you closer to your goals.
Edit: To try and get every setting separated out into it's own chunk of text and not just work on that one policy I had to alter my RegEx a bit. This one's a little more messy with the output, but can be cleaned up simply enough I think. This will split a GPO into individual settings:
$Policies = $CurrentGPOReport -split '(\<(q\d+:.+?>).+?\<(?:\/\2))' | Where { $_ -match ':Name' }
That will get you a collection of things that look like this:
<q1:Account>
<q1:Name>PasswordHistorySize</q1:Name>
<q1:SettingNumber>21</q1:SettingNumber>
<q1:Type>Password</q1:Type>
</q1:Account>
From there you just have to filter for whatever setting you're looking for.
I have tried this with XPath, as you'll have more control navigating in the XML nodes:
[string]$SearchQuery = "user"
[xml]$Xml = Get-GPOReport -Name "Default Domain Policy" -ReportType xml
[array]$Nodes = Select-Xml -Xml $Xml -Namespace #{gpo="http://www.microsoft.com/GroupPolicy/Settings"} -XPath "//*"
$Nodes | Where-Object -FilterScript {$_.Node.'#text' -match $SearchQuery} | ForEach-Object -Process {
$_.Name #Name of the found node
$_.Node.'#text' #text in between the tags
$_.Node.ParentNode.ChildNodes.LocalName #other nodes on the same level
}
After testing we found that in the XML output of the Get-GPOReport cmdlet, the setting names does not always match that of the HTML output. For example: "Log on as a service" is found as "SeServiceLogonRight" in the XML output.

Comparing multiple email address using powershell match

I have a CSV file of 2000 email addresses. I am using PowerShell to check if the user is active in AD. Another developer wrote a PowerShell script for me to do this but he only used the main domain for the email format to match, he didn't add the subdomian that it could have. Some of our email addresses have a 3 part email address after the # symbol.
For example, his code:
foreach ($user in $users) {
Write-Host $user.email
if ($user.email -match "\#mycompany\.com$") {
$status = "NOT FOUND"
# loop through possible AD domains until there is a hit
foreach ($domain in "na","au","eu","as") {
if ($status -eq "NOT FOUND") {
Write-Host " $($domain)" -NoNewline
$status = Get-UserFromEmail -EMail $user.email -ADDomain $domain
Write-Host $status
}
else {
break
}
}
Write-Host
Add-Content -Path $outcsv -Value "$($user.email),$($user.type),`"$($status)`""
}
else {
Add-Content -Path $outcsv -Value "$($user.email),$($user.type),NOT MYCOMPANY"
}
What I need to be able to do is get the match to check if it is a two or three part email address.
#consultant.mycompany.com or #mycompany.com.
Any insight for this PowerShell newbie would be appreciated.
here is one way to test for membership in more than one email domain. all of the domains are all in the same example.com, but they could easily be in testing.com or wizbang.org.
this demos the idea, i presume you can insert it into your script as needed. [grin]
what it does ...
builds a list of email addresses to test
you will get that from your source ... just be sure they are plain strings, not a string inside a property of an object.
builds a domain list
uses the built in regex escape method to escape things like dots as they are needed
adds a $ to the end of each escaped string to anchor the pattern to the end of the email address
uses the escaped strings to build a regex OR of that list
iterates thru the email address list and gets the ones that match one of the domain list items
saves the matches to a $Var
displays the content of that $Var on screen
the code ...
$EmailList = #(
'ABravo#example.com'
'BCharlie#more.example.com'
'CDelta#example.com'
'DEcho#zigzag.papers.com'
'EFoxtrot#even.more.example.com'
)
$DomainList = #(
'#example.com'
'#more.example.com'
'#even.more.example.com'
)
$Regex_DL = $DomainList.ForEach({
[regex]::Escape($_) + '$'
}) -join '|'
$ValidEmailAddressList = $EmailList -match $Regex_DL
$ValidEmailAddressList
output ...
ABravo#example.com
BCharlie#more.example.com
CDelta#example.com
EFoxtrot#even.more.example.com
You can always use the -or operator to chain multiple expressions inside the if condition:
if ($user.email -match "\#mycompany\.com$" -or $user.email -match '#consultant\.mycompany\.com$'){
# ...
}
Alternatively, you can construct a regex pattern that'll match both:
if($user.email -match '#(?:consultant\.)?mycompany\.com$'){
# ...
}
If you're ever unsure about how to escape a literal string in a reguar expression, use [regex]::Escape():
PS C:\> [regex]::Escape('#consultant.mycompany.com')
#consultant\.mycompany\.com

Set-ADuser extensionAttribute won't work but things like title will

I am writing a simple script that takes an already created user and updates an attribute based on what the admin put in.
The code works just fine if I replace extensionAttribute with for example title or something like that, but it won't with extensionAttributes.
I have tried a few things and other extensionAttributes but the code is so simple and it works with other Attributes. I am guess extensionAttributes require a bit more in the code that I am missing.
$name = Read-Host "AD Logon Name"
$key = Read-Host "Azure Key"
Set-ADUser $name -extensionAttribute6 $key -PassThru
Set-ADUser : A parameter cannot be found that matches parameter name 'extensionAttribute6'
Even though it exists it is not finding it.
Set-ADUser has a limited set of parameters covering the most commonly used attributes in AD. However, given the sheer amount of existing attributes and the fact that the AD schema is extensible, an attempt to have all attributes represented as parameters just wouldn't be feasible.
For attributes that are not represented as parameters use the parameter -Add or -Replace with a hashtable argument.
Set-ADUser $name -Replace #{'extensionAttribute6' = $key} -PassThru
Old thread, but this worked for me:
Import-Csv -Path "C:\data\12345.csv" |ForEach-Object {
Set-ADUser $_.samAccountName -replace #{
description = "$($_.description)"
extensionAttribute1 = "$($_.extensionAttribute1)"
extensionAttribute3 = "$($_.extensionAttribute3)"
initials = "$($_.initials)";
#additionalAttributeName = "$($_.additionalAttributeName)"
#additionalAttributeName = "$($_.additionalAttributeName)"
#additionalAttributeName = "$($_.additionalAttributeName)"
#additionalAttributeName = "$($_.additionalAttributeName)"
#additionalAttributeName = "$($_.additionalAttributeName)"
}
}
The top row of your .csv file would look like the following for this example:
samAccountname,description,extensionAttribute1,extensionAttribute3,initials

Is there a way to capture which input objects have no results in Get-ADUser while using Filter?

I have a list of potential account ids. I would like to use Get-ADUser to query if the account or a variation of it exists in the environment. I would then like to capture the data including which accounts ids from my original list don't have any accounts in the environment.
I have successfully captured data for account ids that have an account or a variation of the account id in the AD environment. I am having difficultly with capturing the account ids from my original list that do not produce any results using Get-ADUser
foreach ($user in $inputdata)
{$user = $user + "*"
$(try {Get-ADUser -filter {SamAccountName -like "$user"} -properties Description} catch {$null}) | % {if ($_ -ne $null) {[pscustomobject]#{"ID"=$_.SamAccountName;"DN"=$_.DistinguishedName;"Desc"=$_.Description}}
else {$noaccount += $user}
}
My pscustomobject populates properly with data from everyone that does have an account. But there are no values in $noaccount even though there are ids in my list that do not have accounts in the environment. What should I do to capture the instances which do not have accounts using Get-ADUser?
Also, no error is outputted.
The following should achieve what you want.
$noaccount = [Collections.Generic.List[String]] #()
foreach ($user in $inputdata) {
$userToCheck = Get-ADUser -Filter "SamAccountName -like '$user*'" -properties Description
if ($userToCheck) {
[pscustomobject]#{"ID"=$userToCheck.SamAccountName
"DN"=$userToCheck.DistinguishedName
"Desc"=$userToCheck.Description
}
}
else {
$noaccount.Add($user)
}
}
Explanation:
$noaccount is initialized as a generic list of strings so that we can use the .Add() method rather than the inefficient += operator.
$userToCheck will contain a found user object or $null depending on whether the query found a result. If a user is found, the if condition is $true and your custom object is output. If no user is found, the else condition is triggered and the data stored in $user is added to the $noaccount collection.
I changed the -Filter slightly to remove the script block notation because it is not a script block. The online documentation of the command teaches bad habits by demonstrating the use of script block notation. Instead the filter should be surrounded by double quotes with the values on the inside surrounded by single quotes. The double quotes will allow for PowerShell interpolation to expand variable within. The single quotes will be passed in literally so that the value is interpreted as a string by Get-ADUser.
With your attempt, the try {} block would rarely throw an error and would not throw an error just because an account was not found. You would have to remove the -Filter in favor of the -Identity parameter to produce errors when no object is found. You will still see errors if there are connectivity issues between your session and the domain server though. When your Get-ADUser command produced no output, nothing would get piped into the the foreach {} script block. Therefore, your if {} else {} would never be evaluated.
Enhancement Considerations:
Following some insight provided by Lee_Dailey, instead of adding the not found accounts to a separate collection, you could incorporate them into your custom object output. Maybe you could add a new property that states whether or not they are found. See below for an example:
$noaccount = [Collections.Generic.List[String]] #()
foreach ($user in $inputdata) {
$userToCheck = Get-ADUser -Filter "SamAccountName -like '$user*'" -properties Description
if ($userToCheck) {
[pscustomobject]#{"User" = $user
"ID"=$userToCheck.SamAccountName
"DN"=$userToCheck.DistinguishedName
"Desc"=$userToCheck.Description
"In_AD" = "Yes"
}
}
else {
[pscustomobject]#{"User" = $user
"ID"=$null
"DN"=$null
"Desc"=$null
"In_AD" = "No"
}
}
}

Powershell to Validate Email addresses

I'm trying to get Powershell to validate email addresses using Regex and put email addresses into good and bad csv files. I can get it to skip one line and write to file, but cannot get it to target the email addresses and validate them, then write lines to good and bad files. I can do it in C# and JavaScript, but have never done it in Powershell. I know this can be done, but not sure how.
Here is what I have so far:
Function IsValidEmail {
Param ([string] $In)
# Returns true if In is in valid e-mail format.
[system.Text.RegularExpressions.Regex]::IsMatch($In,
"^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|
(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
}
## Now we need to check the original file for invalid and valid emails.**
$list = Get-Content C:\Emails\OriginalEmails\emailAddresses.csv
# This way we also use the foreach loop.
##======= Test to see if the file exists ===========
if (!(Test-Path "C:\Emails\ValidEmails\ValidEmails.csv")) {
New-Item -path C:\Emails\ValidEmails -name ValidEmails.csv -type
"file" # -value "my new text"
Write-Host "Created new file and text content added"
}
else {
## Add-Content -path C:\Share\sample.txt -value "new text content"
Write-Host "File already exists and new text content added"
}
if (!(Test-Path "C:\Emails\InValidEmails\InValidEmails.csv")) {
New-Item -path C:\Emails\InValidEmails -name InValidEmails.csv -type
"file" # -value "my new text"
Write-Host "Created new file and text content added"
}
else {
# Add-Content -path C:\Emails\ValidEmails -value "new text content"
Write-Host "File already exists and new text content added"
}
#$Addresses = Import-Csv "C:\Data\Addresses.csv" -Header
Name, Address, PhoneNumber | Select -Skip 1
$EmailAddressImp = Import-Csv
"C:\Emails\OriginalEmails\emailAddresses.csv" -Header
FirstName, LastName, Email, Address, City, State, ZipCode | Select
FirstName, LastName, Email, Address, City, State, ZipCode -Skip 1
I'm validating the third column "Email" in the original csv file and trying to write out the whole row to file (good file, bad file). Not sure how to buffer either doing this.
ForEach ($emailAddress in $list) {
if (IsValidEmail($emailAddress)) {
"Valid: {0}" -f $emailAddress
Out-File -Append C:\Emails\ValidEmails\ValidEmails.csv -Encoding UTF8
$EmailAddressImp | Export-Csv "C:\Emails\ValidEmails\ValidEmails.csv"
-NoTypeInformation
}
else {
"Invalid: {0}" -f $emailAddress
Out-File -Append C:\Emails\InValidEmails\InValidEmails.csv -
Encoding UTF8
$EmailAddressImp | Export-Csv
"C:\Emails\InValidEmails\InValidEmails.csv" -NoTypeInformation
}
}
I'm trying to get Powershell to validate email addresses using Regex
Don't!
I would recommend against this. Accurately validating email addresses using regular expressions can be much more difficult than you might think.
Let's have a look at your regex pattern:
^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$
In it's current form it incorrectly validates .#domain.tld.
On the other hand, it doesn't validate unicode-encoded internationalized domain names, like user#☎.com (yes, that's a valid email address)
Instead of trying to find or construct a perfect email validation regex pattern, I would use the MailAddress class for validation instead:
function IsValidEmail {
param([string]$EmailAddress)
try {
$null = [mailaddress]$EmailAddress
return $true
}
catch {
return $false
}
}
If the input string is a valid email address, the cast to [mailaddress] will succeed and the function return $true - if not, the cast will result in an exception, and it returns $false.
When exporting the data, I'd consider collecting all the results at once in memory and then writing it to file once, at the end.
If you're using PowerShell version 2 or 3, you can do the same with two passes of Where-Object:
$EmailAddresses = Import-Csv "C:\Emails\OriginalEmails\emailAddresses.csv" -Header FirstName, LastName, Email, Address, City, State, ZipCode | Select -Skip 1
$valid = $list |Where-Object {IsValidEmail $_.Email}
$invalid = $list |Where-Object {-not(IsValidEmail $_.Email)}
If you're using PowerShell version 4.0 or newer, I'd suggest using the .Where() extension method in Split mode:
$EmailAddresses = Import-Csv "C:\Emails\OriginalEmails\emailAddresses.csv" -Header FirstName, LastName, Email, Address, City, State, ZipCode | Select -Skip 1
$valid,$invalid = $list.Where({IsValidEmail $_.Email}, 'Split')
before exporting to file:
if($valid.Count -gt 0){
$valid |Export-Csv "C:\Emails\ValidEmails\ValidEmails.csv" -NoTypeInformation
}
if($invalid.Count -gt 0){
$invalid |Export-Csv "C:\Emails\ValidEmails\InvalidEmails.csv" -NoTypeInformation
}
You can just use the -match operator, instead of calling into the [Regex] class. Here's a simple example, without any wrapper function:
$EmailRegex = '^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$'
$EmailList = #('a#a.com', 'b#b.co', 'm.a#example.il')
foreach ($Email in $EmailList) {
$DidItMatch = $Email -match $EmailRegex
if ($DidItMatch) {
# It matched! Do something.
}
else {
# It didn't match
}
}
FYI, when you use the -match operator, if it returns boolean $true, then PowerShell automatically populates a built-in (aka. "automatic") variable called $matches. To avoid unexpected behavior, you might want to reset this variable to $null during each iteration, or just wrap it in a function as you did in your original example. This will keep the variable scoped to the function level, as long as you don't declare it in one of the parent scopes.
Once you've validated the e-mail address, you can append it to your existing CSV file, using:
Export-Csv -Append -FilePath filepath.csv -InputObject $Email
For efficiency with the available filesystem resources, you'll probably want to buffer a few e-mail addresses in memory, before appending them to your target CSV file.
# Initialize a couple array buffers
$ValidEmails = #()
$InvalidEmails = #()
if ($ValidEmails.Count -gt 50) {
# Run the CSV export here
}
if ($Invalid.Count -gt $50) {
# Run the CSV export here
}
If you need further help, can you please edit your question and clarify what isn't working for you?
Each of the current top 2 answers here has one significant deficiency:
#Trevor's answer would do just fine, until you supply it this:
John Doe <johndoe#somewhere.com>
#Mathias' answer preaches about accommodating exceptional (yet valid) addresses such as those with non-ASCII or no TLD suffix. The following addresses all validate successfully with the [mailaddress] casting:
olly#somewhere | olly#somewhere. | olly#somewhere...com etc
If, like me, you will not be entertaining these edge cases into your email databases, then a combination of both ideas might prove more useful, like so:
function IsValidEmail {
param([string]$Email)
$Regex = '^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$'
try {
$obj = [mailaddress]$Email
if($obj.Address -match $Regex){
return $True
}
return $False
}
catch {
return $False
}
}
Perhaps there is a performance overhead with creating $obj for every email address on a possibly long mailing list. But I guess that's another matter.
You can use the mailaddress type to ensure it meets RFC, but you will likely still want to make sure the domain is valid:
Resolve-DnsName -Name ('vertigoray#example.com' -as [mailaddress]).Host -Type 'MX'
Works well as a validation script for a function parameter:
function Assert-FromEmail {
param(
[Parameter(Mandatory = $true)]
[ValidateScript({ Resolve-DnsName -Name $_.Host -Type 'MX' })]
[mailaddress]
$From
)
Write-Output $From
}
Output examples of that function on success:
PS > Assert-FromEmail -From vertigoray#example.com
DisplayName User Host Address
----------- ---- ---- -------
vertigoray example.com vertigoray#example.com
Output examples of that function on failure:
PS > Assert-FromEmail -From vertigoray#example..com
Assert-FromEmail : Cannot validate argument on parameter 'From'. The " Resolve-DnsName -Name $_.Host -Type 'MX' "validation script for the argument with value "vertigoray#example..com" did not return a result of True. Determine why the validation script failed, and then try the command again.
At line:1 char:24
+ Assert-FromEmail -From vertigoray#example..com
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Assert-FromEmail], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Assert-FromEmail
Here is one to try I wrote up and tested and has not failed me in any environment to date. Not, saying it won't in someone else's, but for me, it's been 100%.
$SomeEmailAddresses = #'
From:JoeBob#yahoo.com,Tom TheCat tcat#snailmail.net,jerry#snailmail.net
To:TulaJane#hotmail.com;JF#gmail.com;tiger#outlook.com;
Doug Tompson DTompson#icloud.com
MailTo:BobsYourUncle#protonmail.com;
johnny.bravo#yahoo.co.uk
'#
(((Select-String -InputObject $SomeEmailAddresses `
-Pattern '\w+#\w+\.\w+|\w+\.\w+#\w+\.\w+\.\w+' `
-AllMatches).Matches).Value)
Rsults
JoeBob#yahoo.com
tcat#snailmail.net
jerry#snailmail.net
TulaJane#hotmail.com
JF#gmail.com
tiger#outlook.com
DTompson#icloud.com
BobsYourUncle#protonmail.com
johnny.bravo#yahoo.co.uk
#postanote
This common email formatting fails
$SomeEmailAddresses = #'
First A. Last first.a.last#gmail.com.
'#
(((Select-String -InputObject $SomeEmailAddresses -Pattern '\w+#\w+\.\w+|\w+\.\w+#\w+\.\w+\.\w+'
-AllMatches).Matches).Value)
Here is the code I use.
The regex does not support the following because the major email players do not support.
Domains as IP addresses.
Space and special characters "(),:;<>#[] inside a quoted string in local-part.
Comments within parentheses in local-part.
$email = "^(?(?=^(?:([a-zA-Z0-9_!#$%&'+-/=?^{|}~]+|[a-zA-Z0-9_!#$%&'*+\-\/=?^{|}~].[a-zA-Z0-9_!#$%&'+-/=?^{|}~][\.a-zA-Z0-9_!#$%&'*+\-\/=?^{|}~]))#[a-zA-Z0-9.-]{1,63}$)[a-zA-Z0-9_.!#$%&'*+-/=?^`{|}~]{1,63}#[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]{2,})+)$"
$email -match $regexPattern