Add "_" before all SMTP addresses in Exchange - email

I'm looking script that help me to add "_" before all SMTP addresses in Exchange.
Let's say we have a user John Doe. John has 3 different SMTP addresses:
john.doe#contoso.com
jdoe#domain.com,
john#contoso.com
I'd like to change disable all features in Exchange (like OWA, ActiveSync and so on), hide his account in GAL and set all his addresses to:
_john.doe#contoso.com
_jdoe#domain.com,
_john#contoso.com
I can do it for primary SMTP but I can't for rest of them :(
For Now I have try such solution:
Set-Mailbox $sam -HiddenFromAddressListsEnabled $true -DomainController $dmc
Set-CasMailbox $sam -OWAEnabled $false -ActiveSyncEnabled $false -MAPIEnabled $false -PopEnabled $false -ImapEnabled $false -DomainController $dmc
mbx = Get-Mailbox $sam -DomainController $dmc | select -expand EmailAddresses | %{$_.SmtpAddress}
foreach ($M in $mbx)
{
[string]$email += "'smtp:_"+$M+"',"
}
but it doesn't works for me. I'm quite fresh in PS so could you please help me.

Multiple issues:
The main problem is that you're generating a list of the new addresses, but you're not modifying the mailbox objects. Just running a foreach loop against results queried from a mailbox isn't going to modify that object. You need to use Set-Mailbox to actually set the new addresses:
Set-Mailbox -EmailAddresses <new_addresses>
The way you're creating the string, it will have a trailing comma. If you want to create a comma-separated string from strings generated by a loop, do it this way:
$email = $(foreach ($m in $mbx) {
"'smtp:_" + $m + "'"
}) -join ','
The loop returns an array of the strings generated by each iteration, and the -join ',' joins them into a comma-separated string.
That said, you actually don't want to join them into a comma-separated string, because the -EmailAddresses parameter of Set-Mailbox takes an array argument. So, you just want to use the return value of the loop as the argument.
Adding the smtp: prefix is unnecessary because that's the default.
On the other hand, if you're only modifying SMTP addresses, you do want to use Where-Object to filter the contents of the EmailAddresses property for SMTP addresses:
?{$_.Prefix.ToString() -eq 'smtp'}
Putting it all together:
foreach ($mbx in (Get-Mailbox <whatever>)) {
$modified_addresses = $mbx.EmailAddresses `
| ?{$_.Prefix.ToString() -eq 'smtp'} `
| select -ExpandProperty EmailAddresses `
| %{"_$_"}
Set-Mailbox $mbx -EmailAddresses $modified_addresses
}

Related

Whitespace & $null variable handling in PowerShell?

I'm trying to create a script that will grab all users in an OU and change the current primary alias to a secondary alias while adding a new primary smtp address and retaining any other secondary aliases. We have users with 0 aliases, some with 1, some with 2, and some with 3. I am running into an issue when any one of the $sp1, $sp2, $sp3, $sp4, $sp5 are either white space or null. I'm still learning powershell so I'm not sure how to handle that without a lot of pain lol.
$Users = Get-AdUser -Filter * -SearchBase "OU=TestScriptedSMTPAddrChange,OU=***,DC=***,DC=com" -Properties proxyAddresses | Select-Object SamAccountName, proxyAddresses #Change first OU= to the OU you want to change
Foreach ($SAN in $Users){
$SecondaryProxyRaw = $SAN.proxyAddresses #grab proxyAddresses and dump them
$SecondaryProxyRed = $SecondaryProxyRaw.replace('SMTP','smtp') #change primary SMTP addr to secondary smtp addr
$sp1,$sp2,$sp3,$sp4,$sp5 = $SecondaryProxyRed.split(" ",1) #split the proxyAddresses array into variables
$NewPrimaryProxy = "SMTP:$($SAN.SamAccountName)#newdomain.com"} #assign new primary SMTP address
Set-ADUser -Identity $SAN.SamAccountName -replace #{proxyAddresses = "$NewPrimaryProxy","$sp1","$sp2","$sp3","$sp4","$sp5"}
}
Get-AdUser -Filter * -SearchBase "OU=TestScriptedSMTPAddrChange,OU=***,DC=***,DC=com" -Properties proxyAddresses | Select-Object SamAccountName, UserPrincipalName, #{Name="Proxyaddresses";Expression={$_.proxyAddresses -join "*"}}
You should not rely on a user having 1, 3 or perhaps 77 addresses in its proxyAddresses attribute by trying to split these into a fixed number of variables.
Just get them all, replace the uppercase SMTP: into lowercase 'smtp:', filter out the possible one that would be equal to the new proxy address and add the new primary address to the array.
Then, replace the entire proxyAddresses array with the strongly typed (i.e. cast to string[]]) new array.
$Users = Get-AdUser -Filter * -SearchBase "OU=TestScriptedSMTPAddrChange,OU=***,DC=***,DC=com" -Properties proxyAddresses
foreach ($SAN in $Users) {
$NewPrimaryProxy = 'SMTP:{0}#newdomain.com' -f $SAN.SamAccountName
# if you like you can sort the proxies but for the system this will have no effect
$proxies = #($SAN.ProxyAddresses -replace '^SMTP:', 'smtp:' | Where-Object { $_ -ne $NewPrimaryProxy }) + $NewPrimaryProxy
# Note: proxyAddresses needs a Strongly typed string array, that is why we cast $proxies array with [string[]]
$SAN | Set-ADUser -Replace #{proxyAddresses = [string[]]$proxies}
}
.split(" ",1)
doesn't split at all - by definition it returns the input string as-is, because you're asking for 1 token only - see the docs for the .NET [string] type's .Split() method.
To split by runs of whitespace, you can use the unary form of PowerShell's -split operator:
# Split by whitespace and collect tokens in an array.
# -ne '' filters out empty elements, so that if
# $SecondaryProxyRed is effectively empty, $sps becomes an empty array.
$sps = -split $SecondaryProxyRed -ne ''
You can then create an array with $NewPrimaryProxy as the first element, followed by the elements of $sps, if any:
Set-ADUser -Identity $SAN.SamAccountName -replace #{
proxyAddresses = #($NewPrimaryProxy) + $sps
}

How come my condition determining if a user is in a group always returns as false?

I'm writing a script that requires detecting if the executing user account is a domain admin. I do this by getting the current user and checking if they are in the Domain Admins security group.
#Get current user
$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name | Out-String
$CurrentUser = $CurrentUser -replace 'DOMAIN\\'
#Get list of Domain Admins members
$DomainAdmins = Get-ADGroupMember -Identity "Domain Admins" -Recursive | Select -ExpandProperty SamAccountName | Out-String
#Relevant condition
If ($DomainAdmins -like ($CurrentUser)) {
Write-Output "You're a domain admin." #example
}
Else {
Write-Output "You're not a domain admin."
}
Without fail, this script always runs the Else code when run from our domain controller using a domain administrator account.
I have also tried using -contains and .contains() with the exact same results. I've verified that the $CurrentUser value represents the current user accurately and that $DomainAdmins lists out the expected list of users.
I can also do this:
if ($DomainAdmins -contains ("USERNAME")) {Write-host "true"}
Where USERNAME is the current user typed out directly (case-correct or not) and it correctly returns true when that user is a member of the group.
Try with this:
$userSID = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
$DomainAdmins = Get-ADGroupMember -Identity "Domain Admins" -Recursive
if($DomainAdmins.SID.Contains($userSID))
{
Write-Output "You're a domain admin."
}
...
# OR
if($userSID -in $DomainAdmins.SID)
{
Write-Output "You're a domain admin."
}
...
# OR
if($DomainAdmins.SID -contains $userSID)
{
Write-Output "You're a domain admin."
}
The Out-String on Get-ADGroupMember is converting your array into a string which is why you can't use it as comparison:
PS /> #(
'one'
'two'
'three'
) -contains 'one'
True
PS /> (#(
'one'
'two'
'three'
) | Out-String) -contains 'one'
False
An alternative, instead of using Get-ADGroupMember:
$userSID = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
$domainAdminsDN = (Get-ADGroup -Identity "Domain Admins").DistinguishedName
$recursiveMembers = Get-ADUser -LDAPFilter "(memberOf:1.2.840.113556.1.4.1941:=$domainAdminsDN)"
if($recursiveMembers.SID.Contains($userSID))
{
Write-Output "You're a domain admin."
}
...
...
My preferred way to do this is by checking the MemberOf property of the user. It works a little better to keep them as AD objects when you have multiple domains or other oddities:
# check if DA
$DAdn = (Get-ADGroup 'Domain Admins').distinguishedname
If ( Get-ADUser -LDAPFilter "(&(SamAccountName=$Env:USERNAME)(MemberOf:1.2.840.113556.1.4.1941:=$DAdn))" ) {
Write-Output "You're a domain admin." #example
}
Your $DomainAdmins variable is a single string with the members instead of a list object. Using ... | Select -ExpandProperty SamAccountName is enough to get your list without Out-String.
-contains Looks for exact matches in collections, not for substrings.
You have to either remove Out-String from $DomainAdmins, to make it a list and make sure that the name matches entirely, or use .Contains(...) to look for a substring inside the $DomainAdmins string

Powershell Pipeline Argument not gettings passed to Set-ADUser

I've been banging my head against the wall for the past few hours, I'm sure it's because I don't quite understand something about how #{} and $_ work.
First the code:
Get-ADUser username -Properties mail | Set-ADUser -replace #{"proxyaddresses"="SMTP:"+$_.mail}
As you can see I'm trying to update the proxyaddresses fields with the user's email address.
Instead only the string is pulled:(output: proxyaddresses : {SMTP:}) and the pipeline is ignored, I'm assuming it's because it's empty for some reason, but it's not clear to me why.
I've tried variations such as "proxyaddresses="SMTP:$($_.mail)" I tried using default properties that are always sent with Get-ADUser such as UserPrincipalName
I know that something like this is possible because of this http://www.itprotoday.com/management-mobility/more-flexible-active-directory-one-liner and mutiple answers on SO using some variation of the linked example.
When I assign a variable to SMTP:$_.mail and then use that in the field instead like so:
Get-ADUser username -Properties mail | %{ $smtp = "SMTP:"+$_.mail
$_|Set-ADUser -replace #{"proxyAddresses"=$smtp}
This works (output: proxyaddresses :{SMTP:emailaddress#domain.com}). If I leave out the string like so: #{"proxyaddresses"=$_.mail}
I get the following error:
Set-ADUser : Cannot bind parameter 'Replace' to the target. Exception setting "Replace": "Object reference not set to an instance of an object."
At line:1 char:58
I'm not sure what this means.
I'd like some variation of my initial idea to work, but I'll settle for the workaround using an extra variable if there's no other way.
EDIT: There seems to be some confusion about what I'm asking, so I'll clarify:
Is there a way to use the pipeline variable $_ without a script block inside a hashtable, (inside a script block requires double piping like #TheIncorrigible1 suggested in his first answer.)?
EDIT: Based on this it seems this should not be having any issues.
You need to use ForEach-Object to access the pipeline in the way you're trying, otherwise it doesn't know what your pipeline object ($_) is:
Get-ADUser -Identity username -Properties mail |
ForEach-Object {
$_ | Set-ADUser -Replace #{ 'proxyaddresses' = 'SMTP:' + $_.mail }
}
Or the -PipelineVariable common parameter which explicitly assigns $_ to a variable:
Get-ADUser -Identity username -Properties mail -PipelineVariable user |
Set-ADUser -Replace #{ ProxyAddresses = "SMTP:$($user.mail)" }
ProxyAddresses is an array where the Primary Email address is set like SMTP:primary#example.com but there can and will be other elements there too like alias email addresses (that have the lowercase smtp: prefix), SIP: addresses etc.
NEVER try to simply overwrite whatever is already there by a single string found in the mail attribute of the user object, but merge them with the ones you want to add. Selectively replace the ones you want to be changed and build an array of valid addresses.
Basically you do
$oldErrorAction = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
$user = Get-AdUser -Identity $SAMAccountName -Properties mail
$primaryEmailAddress = $user.mail
$externalAddress = "smtp:<WHATEVER ALIAS YOU WOULD LIKE FOR THE USER">
$mailProxies = #("SMTP:$primaryEmailAddress", "smtp:$externalAddress")
# add more to this array if need be
$newProxies = #{'ProxyAddresses' = $mailProxies}
try {
$user | Set-ADUser -Clear ProxyAddresses
$user | Set-ADUser -Add $newProxies
}
catch {
Write-Warning "Could not set ProxyAddresses: $($_.Exception.Message)"
}
$ErrorActionPreference = $oldErrorAction
You may want to try set the variable first and then call on it in the script.
something like this,
$user = Get-ADUser username -Properties mail | select-object mail
Then write your script and call on the variable you may need to use foreach with a if statement to get it to function the way you want.

Find AD-user by Email

I'm trying to get users from active directory, who have a certain email address.
I've got a CSV-File, which contains the emails.
$data = import-csv -path .\are.csv -delimiter +
foreach($i in $data)
{
get-aduser -filter {UserPrincipalName -like '$i.email'}
}
My problem is, that I don't get any output. No error, no data. When I replace $i.email with one of the email addresses, I get the right user information for the one user. So hard coding works.
When I do:
foreach($i in $data)
{
$i.email
}
I get a list of all the emails.
What is wrong with the code?
Your variable is within single quotes thus doesn't get populated. You have to three options to fix that:
Use double quotes with a sub expression $():
UserPrincipalName -like "$($i.email)"
Just omit the quotes:
UserPrincipalName -like $i.email
And finally you could use a format string (even with single quotes):
UserPrincipalName -like ('{0}' -f $i.email)
Here is an example to demonstrate what actual value gets passed using the Write-Host cmdlet:
$data =
#'
email, id
myname#web.de, 1
'# | convertfrom-csv
foreach($i in $data)
{
Write-Host '$i.email' # This is your current approach
Write-Host "$($i.email)"
Write-Host $i.email
Write-Host ('{0}' -f $i.email)
}
Output:
$i.email # This is the output of your current approach
myname#web.de
myname#web.de
myname#web.de

Powershell Modify Emailaddresses in Exchange 2010

I am trying add text before all the email address, instead of that it overwriting and creating one copy of email address of the last. Any ideas what is causing this?
$UD = Get-Mailbox -Identity $_identity
$SmtpAdd=$UD|select -ExpandProperty EmailAddresses|Select SmtpAddress
foreach($address in $SmtpAdd)
{
$Changed="Disabled_"+$($address.SmtpAddress)
Set-Mailbox $_identity -EmailAddresses $Chnged -EmailAddressPolicyEnabled $true
}
expecting output: Disabled_rave#in.com,Disabled_raj#in.com
but it is giving: Disabled_raj#in.com,raj#in.com.
It is not adding Disabled in all the mails.
You actual results indicate that $($address.SmtpAddress) is a string. In such case, you are combing two strings:
"a" + "b,c" and the results of this operation will be "ab,c"
So you need to split $($address.SmtpAddress) by ',' ,add "Disabled_" for each element, store all new email address inside array and then join those elements as string using ',':
[array]$Changed = $null
$($address.SmtpAddress) -split ',' | % {
$Changed += "Disabled_"+ $_
}
$Changed = $Changed -join ','
Set-Mailbox $_identity -EmailAddresses $Chnged -EmailAddressPolicyEnabled $true
Hope that this will help you.