<# .SYNOPSIS Copies group memberships from one user to another in Microsoft 365 with detailed logging. .DESCRIPTION This script connects to Exchange Online and copies all group memberships (both Microsoft 365 Groups and Distribution Lists) from a source user to a destination user, with comprehensive logging. .NOTES File Name : Copy-365GroupMembershipsV3.ps1 Prerequisite : ExchangeOnlineManagement PowerShell module Approved by SSE Chris Proudmore - 11/04/2025 - While working on #2627086 #> # Input parameters $source = "user@domain.com.au" $destination = "user@domain.com.au" # Logging configuration $logFolder = "C:\Logs\365UserCopy" $logDate = Get-Date -Format "ddMMyyyy_HHmm" $logFile = "$logFolder\365UserGroupCopy_$logDate.log" # Create log directory if it doesn't exist if (-not (Test-Path $logFolder)) { New-Item -ItemType Directory -Path $logFolder -Force | Out-Null } # Function for consistent logging function Write-Log { param ( [string]$Message, [string]$Level = "INFO" ) $timestamp = Get-Date -Format "dd-MM-yyyy HH:mm:ss" $logEntry = "[$timestamp] [$Level] $Message" Write-Output $logEntry | Out-File -FilePath $logFile -Append -Encoding utf8 # Also display to console switch ($Level) { "ERROR" { Write-Host $logEntry -ForegroundColor Red } "WARNING" { Write-Host $logEntry -ForegroundColor Yellow } "SUCCESS" { Write-Host $logEntry -ForegroundColor Green } default { Write-Host $logEntry } } } try { # Start logging Write-Log "Script started" Write-Log "Source user: $source" Write-Log "Destination user: $destination" # Connect to Exchange Online Write-Log "Connecting to Exchange Online..." Connect-ExchangeOnline -ShowBanner:$false -ErrorAction Stop Write-Log "Successfully connected to Exchange Online" # Get source user details Write-Log "Retrieving source user information..." $sourceMailbox = Get-EXOMailbox $source -ErrorAction Stop if (-not $sourceMailbox) { throw "Source user $source not found" } $dn = $sourceMailbox.DistinguishedName Write-Log "Source user found: $($sourceMailbox.PrimarySmtpAddress) (DN: $dn)" # Process Microsoft 365 Groups Write-Log "Processing Microsoft 365 Groups..." $o365Groups = Get-EXORecipient -Filter "Members -eq '$dn'" -RecipientTypeDetails GroupMailbox -ErrorAction Stop Write-Log "Found $($o365Groups.Count) Microsoft 365 Groups for source user" foreach ($group in $o365Groups) { try { Write-Log "Adding $destination to group: $($group.Name) ($($group.PrimarySmtpAddress))" Add-UnifiedGroupLinks -Identity $group.Name -LinkType Member -Links $destination -ErrorAction Stop Write-Log -Level "SUCCESS" "Successfully added $destination to Microsoft 365 Group: $($group.Name)" } catch { Write-Log -Level "ERROR" "Failed to add $destination to Microsoft 365 Group $($group.Name): $_" } } # Process Distribution Groups Write-Log "Processing Distribution Groups..." $distGroups = Get-EXORecipient -Filter "Members -eq '$dn'" -RecipientTypeDetails MailUniversalDistributionGroup -ErrorAction Stop Write-Log "Found $($distGroups.Count) Distribution Groups for source user" foreach ($group in $distGroups) { try { Write-Log "Adding $destination to distribution group: $($group.Name) ($($group.PrimarySmtpAddress))" Add-DistributionGroupMember -Identity $group.Name -Member $destination -ErrorAction Stop Write-Log -Level "SUCCESS" "Successfully added $destination to Distribution Group: $($group.Name)" } catch { Write-Log -Level "ERROR" "Failed to add $destination to Distribution Group $($group.Name): $_" } } # Summary report Write-Log "Processing complete" Write-Log "SUMMARY:" Write-Log " Microsoft 365 Groups processed: $($o365Groups.Count)" Write-Log " Distribution Groups processed: $($distGroups.Count)" } catch { Write-Log -Level "ERROR" "Script encountered an error: $_" Write-Log -Level "ERROR" "Error details: $($_.ScriptStackTrace)" } finally { # Disconnect from Exchange Online try { Write-Log "Disconnecting from Exchange Online..." Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue Write-Log "Disconnected from Exchange Online" } catch { Write-Log -Level "WARNING" "Failed to properly disconnect from Exchange Online: $_" } Write-Log "Script completed" Write-Log "Log file saved to: $logFile" }