Can I get a list of DLs a set of users in a CSV are in with PS?

Hello all!
I have no idea if this is doable or not but how would I go about running a command in PS that says everyone in my CSV file under the column UserPrincipalName please find me with Distribution list they are part of in my Office 365 Tenant?

Is this something that is achievable?

PS As usual, thank you very much in advance for all the help. One day I will be as good as some of the gurus here.

Thanks!

Short answer: Yes. :wink:

Get all DLs of your tenant including their members, save them temporarily in a variable and run a loop over your CSV items with a nested loop over the DLs comparing the users from your CSV to the members of the DLs and you’re done. :+1:t4: :wink:

BTW: have you actually tried to find a solution before you came here to ask for help? :smirk:

I did hunt but it’s how do I get it done that eludes me.
I’m by no means a PS expert (hence why I come here :slight_smile: )

That’s ok - that’s why we’re here for. :+1:t4: But since this is not a free code shop we expect a minimum effort from you to get your problem solved or to get your task done in the first place. So if you already have some code you’ve just got stuck with - please share it here (formatted as code) and we will be pleased to try to help you to step further. :wink:

Sure let me go post it (I have to find it)
I had a command where I would use to get DL lists. I thought I could build on that
Ill be back shortly :slight_smile:

Hello!
Apologies for the delay I actually didn’t get a chance to come back until today.
I was following this script but it’s not doing exactly what I want it to do.

<#
=============================================================================================
Name:           Export Distrbution Groups a user is member of
Description:    This script exports all users and their distribution group membership
Website:        o365reports.com
Script by:      o365reports Team
For detailed script execution: https://o365reports.com/2022/04/19/list-all-the-distribution-groups-a-user-is-member-of-using-powershell/
============================================================================================
#>
Param
(
    [string]$UserName=$Null,
    [string]$Password=$Null,
	[string]$UserPrincipalName=$Null,
	[string]$InputCsvFilePath=$Null
)
Function Connect_Exo
{		
    #Check for EXO v2 module inatallation
    $Module = Get-Module ExchangeOnlineManagement -ListAvailable
    If($Module.count -eq 0) 
    {			
        Write-Host "Exchange Online PowerShell V2 module is not available"  -ForegroundColor yellow  
        $Confirm= Read-Host "Are you sure you want to install module? [Y] Yes [N] No" 
        If($Confirm -match "[yY]")			   
        {						
            Write-host "Installing Exchange Online PowerShell module"
            Install-Module ExchangeOnlineManagement -Repository PSGallery -AllowClobber -Force
        } 
        Else 
        { 
            Write-Host EXO V2 module is required to connect Exchange Online.Please install module using Install-Module ExchangeOnlineManagement cmdlet. 
            Exit
        }
    }   
    #Importing Module by default will avoid the cmdlet unrecognized error
    Import-Module ExchangeOnlineManagement
    Write-Host Connecting to Exchange Online...
    #Storing credential in script for scheduling purpose/ Passing credential as parameter - Authentication using non-MFA account
    If(($UserName -ne "") -and ($Password -ne ""))
    {			
        $SecuredPassword = ConvertTo-SecureString -AsPlainText $Password -Force
        $Credential  = New-Object System.Management.Automation.PSCredential $UserName,$SecuredPassword
        Connect-ExchangeOnline  -Credential $Credential
    }
    Else
    {
        Connect-ExchangeOnline
    }
} 

Function Import_Csv
{
    #Importing UserPrincipalName From The Csv
    Try
    {
        $UserDetails=@()
        Write-Host "Importing UserPrincipalNames from Csv..."
        $UPNs=Import-Csv $InputCsvFilePath 
        foreach ($UPN in $UPNs)
        {
        $UserPrincipalName=$UPN.User_Principal_Name
            Try
	        {   
		         Get-Mailbox -Identity $UserPrincipalName -ErrorAction Stop |foreach{
                     List_DLs_That_User_Is_A_Member
                   
	        }
}
	        Catch
            {
		        Write-Host "$UserPrincipalName is not a valid user"
	        }
        }

    }
    catch
    {
        Write-Host "$InputCsvFilePath is not a valid file path"
    }     
}
Function List_DLs_That_User_Is_A_Member
{
    #Finding Distribution List that  User is a Member
	$Result= @()
    $DistinguishedName=$_.DistinguishedName
    $Filter = "Members -Like ""$DistinguishedName"""
    $UserPrincipalName=$_.UserPrincipalName
    $UserDisplayName=$_.DisplayName
    Write-Progress -Activity "Find Distribution Lists that user is a member" -Status "Processed User Count: $Global:ProcessedUserCount" -CurrentOperation "Currently Processing in  $UserPrincipalName"
    $DLs=Get-DistributionGroup -ResultSize Unlimited -Filter $Filter
    $GroupCount=$DLs | Measure-Object | select count
    If($GroupCount.count -ne 0)
    {    
	    $DLsCount=$GroupCount.count
		$DLsName=$DLs.Name
	    $DLsEmailAddress=$DLs.PrimarySmtpAddress
    }
    Else
    {
	    $DLsName="-"
	    $DlsEmailAddress="-"
		$DLsCount='0'
    }
    $Result=New-Object PsObject -Property @{'User Principal Name'=$UserPrincipalName;'User Display Name'=$UserDisplayName;'No of DLs that user is a member'=$DLsCount;'DLs Name'=$DLsName -join ',';'DLs Email Adddress'=$DLsEmailAddress -join ',';} 
    $Result|Select-Object 'User Principal Name','User Display Name','No Of DLs That User Is A Member','DLs Name','DLs Email Adddress'| Export-Csv  $OutputCsv -NoTypeInformatio -Append 
    $Global:ProcessedUserCount++		
  
}
Function OpenOutputCsv
{  		
    #Open Output File After Execution 
    If((Test-Path $OutputCsv) -eq "True") 
    {			
        Write-Host "The Output file available in $OutputCsv" 
        Write-Host `nThe output file contains $ProcessedUserCount users. -ForegroundColor Green
        $Prompt = New-Object -ComObject wscript.shell    
        $UserInput = $Prompt.popup("Do you want to open output file?",` 0,"open output file",4)    
        If($UserInput -eq 6)    
        {    
            Invoke-Item "$OutputCsv"    
        }  
    } 	
}
Connect_Exo
$Global:ProcessedUserCount=1
$OutputCsv=".\ListDLs_UsersIsMemberOf_$((Get-Date -format MMM-dd` hh-mm` tt).ToString()).csv"
If($UserPrincipalName -ne "")
{  
	Try
	{
        write-Host "Checking $UserPrincipalName is a valid user or not"
		Get-Mailbox -Identity $UserPrincipalName -ErrorAction Stop|ForEach{
            List_DLs_That_User_Is_A_Member
        }
	}
	Catch
    {
		Write-Host "$UserPrincipalName is not a valid user"
	}
}		
Elseif($InputCsvFilePath -ne "")
{	
    Import_Csv
}
Else
{ 
    Get-Mailbox -ResultSize unlimited -RecipientTypeDetails UserMailbox | ForEach{
	    List_DLs_That_User_Is_A_Member
    }
}
OpenOutputCsv
#Removing connected session
Get-PSSession |Remove-PSSession

The script is doing exactly what I want it to do (TY Olaf for forcing me to go look at other scripts I had) but it only works for users and not contacts. The issue I have is I’m trying to find the DLs that tenant contacts are in and not tenant users.

Sorry if I was unclear.

I googled and found this that could potentially work but when I try it I get an error telling me

The operation couldn't
be performed because 'DistroGroup' matches multiple entries

Here is what I ran

$groups = Get-DistributionGroup -ResultSize unlimited 
foreach($group in $groups){Get-DistributionGroupmember $group | ? {$_.recipienttype -eq 'MailContact' -and $_.distinguishedname -match 'OU=External contacts,DC=yourdomain,DC=com' } | select @{name='GroupName';expression={$group}},name, recipienttype} 

This is the command I used to run it

.\GetDistro.ps1 -InputCsvFilePath “.\distro.csv"`

Hopefully you can point out my error

TY!

If you find a script online there is usually an author you could contact if you have questions or problems with the code. :wink: And since it does not seem to address the information you’re after I will not digg into it for now.

The error message you shared seems incomplete and it seems not to fit to the code you shared. Please always share the complete error message and the exact code you used getting the error message.

Regardless of that: for better readability you should format your code nicely and you should avoid using aliasses.

Here you can read more about that topic:

Hello again Olaf!
You are right I didn’t paste the entire error message (I mostly thought it was jargon)

Write-ErrorMessage : Ex9E65A2|Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException|The operation couldn't
be performed because 'Sales' matches multiple entries.
At C:\Users\Jonathan\AppData\Local\Temp\tmpEXO_51bgzuwq.gdw\tmpEXO_51bgzuwq.gdw.psm1:1099 char:13
+             Write-ErrorMessage $ErrorObject
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Get-DistributionGroupMember], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : [Server=BYAPR02MB5351,RequestId=b306ac61-1380-cb7f-f476-f77313466a92,TimeStamp=Mon, 05 Dec 2022 1
   9:35:41 GMT],Write-ErrorMessage

I should have also been a little clearer in my above message.

The original script I found from o365reports.com does what I want it to do. In fact it is outputting exactly what I want it to output, but it is outputting it for users and not contacts.
Is there any way that I can do exactly what this script is doing but instead of calling the users it calls the contacts?

When I run my CSV with the contacts (on the first script from o365reports) I get this results

contact@externaldomain.com is not a valid user

Which I think is normal since it’s only looking for actual users and not contacts.

The error message changed. :thinking: :face_with_raised_eyebrow: And now it’s obviously thrown by the EXO module - not the script. :thinking: :face_with_raised_eyebrow: And it does not help if you don’t share the according code. :thinking: :face_with_raised_eyebrow:

Again: when you use code or a script you found online you should contact the authors of the script first and ask them for help.

I’d recommend to keep it simple and use code you have the chance to understand. Try if this outputs something useful for you:

$groupList = Get-DistributionGroup -ResultSize unlimited 
foreach ($group in $groupList) {
    Get-DistributionGroupmember $group | 
    Where-Object { $_.recipienttype -eq 'MailContact' } | 
    Select-Object @{Name = 'GroupName'; Expression = {$group} }, Name, Recipienttype
} 

Hello Olaf,
I was able to run a script that does exactly what I wanted it to do.

For reference here is what I ran, it worked exactly how I wanted it to.

$answer = Read-Host "Would you like to list all external contacts and groups they belong to ( y / n )?"
While ("y","n" -notcontains $answer) {
    $answer = Read-Host "Would you like to list all external contacts and groups they belong to ( y / n )?"
}
If ($answer -eq 'y') {
    $collection = @()
    $contacts = Get-MailContact -ResultSize unlimited
    
    ForEach($contact in $contacts) {
        
        $DN=$contact.DistinguishedName
        $Filter = "Members -like '$DN'"
        $groups = Get-DistributionGroup -ResultSize unlimited -Filter $Filter
        $outObject = "" | Select "Full Name","Email","Groups"
        $outObject."Full Name" = $contact.DisplayName
        $outObject."Email" = $contact.PrimarySMTPAddress
        $outObject."Groups" = $groups
        $collection += $outObject
    }
    $collection | Out-GridView
    Remove-Variable * -ErrorAction SilentlyContinue
    
}
    
Else {
    Remove-Variable * -ErrorAction SilentlyContinue
}

It exported exactly what I needed.

TY for your help in guiding me to search for the correct scripts. As usual you are more than helpful! :slight_smile:

This code doesn’t export anything. It simply displays a GridView of the objects

Hello!
It doesn’t export anything to a CSV I just copied whatever came out of the Grid View and put it in an Excel .
It worked exactly the way I wanted it to
TY again for all your help guys it’s super appreciated!

If that works for you, fantastic. I just didn’t want future readers to expect that code to export anything. :slight_smile: