How to automate removing Exchange Contacts in Office365 - powershell

I am writing a powershell script that:
Compares two CSV files
Output files for: Changes, added, removed contacts
Update and add contacts
Remove contacts
The problem is when I try and removed contacts. Which is done by:
#Check for Removed Contacts
foreach($row in $File1_Data )
{
$data_found=0
foreach($id in $emails_id)
{
if ($row.ExternalEmailAddress -eq $id)
{
$data_found=1
}
}
if($data_found -eq 0 ) #Email Not Found
{ $row|Select-Object -Property ExternalEmailAddress|Export-Csv -Path $Removed_Contact -Append -NoTypeInformation}
}
Now I have a file with only the email addresses. The error comes when I try and run the command connected on the exchange server.
$RemoveContacts = Import-CSV ".\Removed Contacts_$((Get-Date).ToString('MMddyyyy')).csv"
$RemoveContacts | ForEach { Remove-MailContact -identity $_ -confirm:$false}
But I get the following error:
Cannot process argument transformation on parameter 'Identity'. Cannot convert the
"#{ExternalEmailAddress=testuser#testcompany.com}" value of type "Deserialized.System.Management.Automation.PSCustomObject"
to type "Microsoft.Exchange.Configuration.Tasks.MailContactIdParameter".
+ CategoryInfo : InvalidData: (:) [Remove-MailContact], ParameterBindin...mationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Remove-MailContact
+ PSComputerName : outlook.office365.com
$File_Data structure is in the format Microsoft requires.
and
$emails_id is the function that compares the two csv files. But that's not where the script breaks, that's just how i create the file.
What am I missing?

The error message is telling you that it can't convert the value of $_ to what it needs for -Identity parameter. Generally the -Identity parameter for most PS commandlets is going to be the human readable unique name of something. In this case, it would be an email address. With that said the error message is telling you that instead of $_ containing the string version of an email address, it contains a hash or dictionary object that contains a single property, ExternalEmailAddress.
So to make this work, change your $_ to $_.ExternalEmailAddress and now the call to Remove-MailContact will use the value of the ExternalEmailAddress property of the object in your ForEach loop.

Related

Find if a user is part of some distribution lists

I want to get a script working in powershell which takes a user's email and look it up against a few distribution lists to see if the user is a part of any of them. It should also check the nested distribution groups if any under the main distribution lists.
here's what I have but can't get it to work. Any help would be appreciated, I am fairly new to this.
# Prompt for user email address
$UserEmail = Read-Host -Prompt 'Please enter the user email address'
# Read the CSV file
$DistributionLists = Import-Csv -Path '.\DLs.csv'
# Loop through each Distribution List
foreach ($DL in $DistributionLists) {
# Get List of Distribution Group Members
$GroupMembers = Get-DistributionGroupMember -Identity $DL -ResultSize Unlimited
# Loop through each member
foreach ($Member in $GroupMembers) {
# Check if the user's email address matches
if ($Member.PrimarySmtpAddress -eq $UserEmail) {
# Output the matches
Write-Output "User $UserEmail is a part of $($DL.Name)"
}
}
}
but i get below error on execution:
Write-ErrorMessage : Cannot process argument transformation on parameter 'Identity'. Cannot convert value "" to type
"Microsoft.Exchange.Configuration.Tasks.DistributionGroupMemberIdParameter". Error: "Parameter values of type Microsoft.Exchange.Configuration.Tasks.DistributionGroupMemberIdParameter can't be empty. Specify a value, and try again.
Parameter name: identity"
At C:\Users\abcd\AppData\Local\Temp\tmpA_hrt0empv.vlz\tmpA_hrt0empv.vlz.psm1:1087 char:13
+ Write-ErrorMessage $ErrorObject
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-DistributionGroupMember], ParameterTransformationException
+ FullyQualifiedErrorId : [Server=BNxxxxxx5601,RequestId=abcdef5-1e51-d5f0-2a56-77b30f23bf3a,TimeStamp=Thu, 09 Feb 2023 14:04:01 GMT],Write-ErrorMessage
Error screenshot
The error statement informs us "-Identify $DL" is empty; $DL returns the entire object row and not just the name to be matched. To correct, refactor to $DL.DistributionLists where "DistributionLists" is the column header in the imported CSV file.
As we confirmed together you have already imported ExchangeOnlineManagement and made the connected to Exchange Online.... I've kept these in the code below for future reader reference.
# Pearl-script below:
# Import the ExchangeOnlineManagement module
Import-Module ExchangeOnlineManagement
# Connect to Exchange Online
Connect-ExchangeOnline
# Prompt for user email address
$UserEmail = Read-Host -Prompt 'Please enter the user email address'
# Read the CSV file
$DistributionLists = Import-Csv -Path '.\DLs.csv'
# Loop through each Distribution List
foreach ($DL in $DistributionLists) {
# Get List of Distribution Group Members
$GroupMembers = Get-DistributionGroupMember -Identity $DL.DistributionLists -ResultSize Unlimited
# Loop through each member
foreach ($Member in $GroupMembers) {
# Check if the user's email address matches
if ($Member.PrimarySmtpAddress -eq $UserEmail) {
# Output the matches
Write-Output "User $UserEmail is a part of $($DL.DistributionLists)"
}
}
}

Value type error during changing of email addresses and aliases via PowerShell ISE

I trying to do bulk change of distribution lists, using Powershell script, but didn't made it far.
I importing new addresses and aliases from a .CSV file and seems like this is the issue. Because when I assigning an address and an alias to an exact DL, then it all fine. Or maybe the script written bad.
Error message:
Cannot process argument transformation on parameter 'EmailAddresses'. Cannot convert value "System.Collections.ArrayList" to type "Microsoft.Exchange.Data.ProxyAddressCollection". Error: "The address
'SMTP:#{DisplayName=TEAM_A; PrimarySmtpAddress=a_dl#company.com; Alias=a_dl#company.org}.PrimarySmtpAddress' is invalid: The address '#{DisplayName=TEAM_A; PrimarySmtpAddress=a_dl#company.com;
Alias=a_dl#company.org}.PrimarySmtpAddress' is not a valid SMTP address.
Parameter name: address
Actual value was #{DisplayName=TEAM_A; PrimarySmtpAddress=a_dl#company.com; Alias=a_dl#company.org}.PrimarySmtpAddress."
+ CategoryInfo : InvalidData: (:) [Set-DistributionGroup], ParameterBindin...mationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Set-DistributionGroup
+ PSComputerName : outlook.office365.com*
The script:
Connect-ExchangeOnline
$psmtpa = Import-Csv C:\_temp\Excange\PrimarySMTPAddress.csv
ForEach ($name in $psmtpa){
ForEach ($email in $psmtpa){
ForEach ($alias in $psmtpa){
Set-DistributionGroup "$name.DisplayName" -EmailAddresses SMTP:$email.PrimarySmtpAddress,$alias.Alias
}
}
}
And the .CSV look like this:
PS And yeah, I trying to perform it, using PowerShell ISE, instead of writing a one-liner in PowerShell app. Also, I tried WindowsEmailAddress, but powershell don't understand it what is it.
There are a few things to note, first, the double quotes on "$name.DisplayName" are stringifying the object $name and appending .DisplayName to it. Remove them.
$object = [pscustomobject]#{
foo = 'var'
}
"$object.foo" # => #{foo=var}.foo
$object.foo # => 'var'
Second thing to note, on:
-EmailAddresses SMTP:$email.PrimarySmtpAddress,$alias.Alias
If you're looking to add a Primary and Secondary SMTP address, as Mathias pointed out, you should be using:
-EmailAddresses "SMTP:$($email.PrimarySmtpAddress)", "smtp:$($email.Alias)"
If instead, $email.Alias is actually an Alias, then you should be using:
-EmailAddresses "SMTP:$($email.PrimarySmtpAddress)" -Alias $email.Alias
See Subexpression operator $( ) for more info.
Lastly, you don't need 3 loops, 1 should be enough to loop over your CSV:
$psmtpa = Import-Csv C:\_temp\Excange\PrimarySMTPAddress.csv
foreach ($object in $psmtpa) {
Set-DistributionGroup $object.DisplayName -EmailAddresses "SMTP:$($object.PrimarySmtpAddress)", "smtp:$($object.Alias)"
}

Update AddresBookPolicy AddressLists based on variable input

I am trying to update an Address Book Policy on Exchange Online.
Idea is that I parse some Address Lists and save these into a variable.
These could be passed into the Set-AddresBookPolicy.
So I start off with parsing these adresses:
$AddressLists = (Get-AddressList).Id | ? {$_ -like "*Company_1*"}
This results an array like \Company_1_Users, \Company_1_Contacts, \Company_1_DLs as expected.
I apply these with
Set-AddressBookPolicy -Identity "Company1" -AddressLists $AddressLists `
-RoomList "C1_Rooms" -GlobalAddressList "C1_GAL" -OfflineAddressBook "C1_OAB"
Result is an error:
WARNING: An unexpected error has occurred and a Watson dump is being generated: The operation can't be performed on this object because its status isn't valid.
The operation can't be performed on this object because its status isn't valid.
+ CategoryInfo : NotSpecified: (:) [Set-AddressBookPolicy], InvalidOperationException
+ FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.Exchange.Management.SystemConfigurationTasks.SetAddressBookPolicy
+ PSComputerName : outlook.office365.com
I've tried converting it to a string (with -join ',') and have tried casting it, but I can't get further then an error (which then is of another kind).
If I copy the output and then type it into the command, it works. So that part is correct. However, I would like to automate this.
Does anyone know how I can correctly provide an input into the below cmdlet and have it running as expected?
EDIT: added full script below:
$AddressLists = #()
$AddressLists = (Get-AddressList).Id | ? {$_ -like "*Company_1*"}
$AddressLists = $AddressLists -join ',' #Adding this line just results in another error...
Set-AddressBookPolicy -Identity "Company1" -AddressLists $AddressLists `
-RoomList "C1_Rooms" -GlobalAddressList "C1_GAL" -OfflineAddressBook "C1_OAB"
The result of $AddressLists is an array (System.Array) with contents:
\Company_1
\Company_1Country1
\Company_1Country2
\Company_1Department1
\Company_1Department2
If your variable produces what you are saying...
$AddressLists = (Get-AddressList).Id | {$_ -like "*Company_1*"}
\Company_1_Users,
\Company_1_Contacts,
\Company_1_DLs
Then In Theory When You Add It Into a ForEach Loop It Should Work Accordingly. I Don't Have Exchange To Test It (by removing $updatecommand and leaving the execution command :o)
Change the settings of an address book policy in Exchange Online
<https://learn.microsoft.com/en-us/exchange/address-books/address-book-policies/change-the-settings-of-an-address-book-policy>
$AddressLists = ("\Company_1_Users", "\Company_1_Contacts", "\Company_1_DLs")
$iD = "Company1"
$rL = "C1_Rooms"
$gAL = "C1_GAL"
$oAB = "C1_OAB"
ForEach($AddressList in $AddressLists){
Write-Host "Without an Exchange Server, I'm Just Demonstating The Update Process"
Write-Host "The AddressList Being Updated Is -- $AddressList"
$updatecommand = "Set-AddressBookPolicy -Identity $iD -AddressLists $AddressList -RoomList $rL -GlobalAddressList $gAL -OfflineAddressBook $oAB"
Write-Host $updatecommand
}

Line break issue when configuring "send on behalf of"

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).

Invoke-expression on a previous script to add parameters to it from a csv

I'm learning powershell only from few month, and I'have been asked to write a script that outsides my actual knowledge.
I have a script which work perfectly when I have to extract user from ONE AD group. My probleme here is that this script accept only one parameter and I have been asked this week to extract over 1000 groups...)
.\myscript.ps1 ADgroup
What I want to do :
1. I get an extract in CSV so I'd like to put in parameters all these groups to my script
2. I want to generate a file text of the result but I am not sure which is the best way to do that in this case.
So Here is my code
$grouplist=IMPORT-CSV C:\Myfile\Mytest\liste.csv
foreach ($group in $grouplist) {
Invoke-Expression .\membre_group_AD_V2.ps1 $group
}
My CSV test had just to group to run my test, I get the same kind of error on each group
Invoke-Expression : Impossible de trouver un paramètre positionnel acceptant l'argument « #{grouplist=Administrators} ».
Au caractère C:\myfile\Mytest\script\dev\bouclage_membre_group_AD_V2.ps1:36 : 2
+ Invoke-Expression .\membre_group_AD_V2.ps1 $group
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument : (:) [Invoke-Expression], ParameterBindingException + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.InvokeExpressionCommand
If required I can try to translate this error. If you can help to improve my knowledge it could be great! Thank you for tanking time to read this.
It seems that you have a "grouplist" header in your CSV file. I assume it looks something like this:
"grouplist"
"Administrators"
"Other Group Name"
Now, when you use Import-Csv, all rows are read into an object with a single NoteProperty per column, named after the headers in the csv file.
So in your case, each object in $grouplist has a grouplist property that contains the name - this is the property value you want to pass as an argument to your script:
$grouplist=Import-Csv C:\Myfile\Mytest\liste.csv
foreach ($group in $grouplist) {
.\membre_group_AD_V2.ps1 $group.grouplist
}
Assuming that the output from membre_group_AD_V2.ps1 is already what you expect, use the Out-File cmdlet with -Append to write it to the same text file:
$grouplist=Import-Csv C:\Myfile\Mytest\liste.csv
$OutfilePath = "C:\Myfile\membre_group_out.txt"
foreach ($group in $grouplist) {
.\membre_group_AD_V2.ps1 $group.grouplist | Out-File $OutfilePath -Append -Encoding utf8
}
If not, you'll have to show us the membre_group_AD_V2.ps1 script