CopyAdGroups.psm1

function Copy-AdGroups {
    <#
    .SYNOPSIS
    Copies or adds Active Directory group memberships from one user to another.
 
    .DESCRIPTION
    The Copy-AdGroups function copies or adds Active Directory group memberships from one user to another.
    Use the -Clone switch to copy memberships exactly, removing any groups from the target user that the source user is not a part of.
    Use the -Add switch to add the target user to the groups the source user is part of, while keeping any pre-existing groups.
    The -Type parameter allows you to specify which types of groups to copy: DistributionOnly, SecurityOnly, or Both (default).
 
    .PARAMETER Source
    The source user whose group memberships will be copied.
 
    .PARAMETER Target
    The target user who will be added to the groups.
 
    .PARAMETER Clone
    Switch to copy memberships exactly, removing any groups from the target user that the source user is not a part of.
 
    .PARAMETER Add
    Switch to add the target user to the groups the source user is part of, while keeping any pre-existing groups.
 
    .PARAMETER Type
    Specify which types of groups to copy: DistributionOnly, SecurityOnly, or Both. Default is Both.
 
    .PARAMETER LogFilePath
    The path to the log file where actions will be recorded. Default is "C:\Logs\GroupCopy.log".
 
    .EXAMPLE
    Copy-AdGroups -Source "User1" -Target "User2" -Clone
    This command clones the group memberships from User1 to User2, removing any groups from User2 that User1 is not a part of, for both distribution and security groups.
 
    .EXAMPLE
    Copy-AdGroups -Source "User1" -Target "User2" -Add -Type SecurityOnly
    This command adds User2 to any security groups that User1 is a part of, without removing any pre-existing groups from User2.
 
    .EXAMPLE
    Copy-AdGroups -Source "User1" -Target "User2" -Clone -Type SecurityOnly
    This command clones the security group memberships from User1 to User2, removing any security groups from User2 that User1 is not a part of, while keeping distribution groups.
 
    .EXAMPLE
    $list = @("User3", "User4", "User5")
    Foreach ($user in $list) {
        Copy-AdGroups -Source "User1" -Target $user -Add -Type DistributionOnly
    }
    This example adds multiple users (User3, User4, User5) to the distribution groups that User1 is part of.
 
    .EXAMPLE
    $list | ForEach-Object {
        Copy-AdGroups -Source "User1" -Target $_ -Add
    }
    This example demonstrates using the function in a pipeline to add multiple users to the groups (both security and distribution) that User1 is part of.
 
    .NOTES
    Author: Cody Gore
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]$Source,

        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string]$Target,

        [Parameter()]
        [ValidateSet("DistributionOnly", "SecurityOnly", "Both")]
        [string]$Type = "Both",

        [Parameter(Mandatory=$true, ParameterSetName="Clone")]
        [switch]$Clone,

        [Parameter(Mandatory=$true, ParameterSetName="Add")]
        [switch]$Add,

        [Parameter()]
        [string]$LogFilePath = "C:\Logs\GroupCopy.log"
    )

    process {
        try {
            # Ensure log directory exists
            $logDirectory = Split-Path $LogFilePath
            if (-not (Test-Path -Path $logDirectory)) {
                New-Item -Path $logDirectory -ItemType Directory -Force
            }

            # Set the script scope variable for the log file path
            $script:logFilePath = $LogFilePath

            # Get the groups for the Source user
            $sourceGroups = Get-ADUser -Identity $Source -Property MemberOf | Select-Object -ExpandProperty MemberOf

            # Filter source groups based on type
            if ($Type -eq "DistributionOnly") {
                $sourceGroups = $sourceGroups | Where-Object {
                    (Get-ADGroup -Identity $_).GroupCategory -eq 'Distribution'
                }
            } elseif ($Type -eq "SecurityOnly") {
                $sourceGroups = $sourceGroups | Where-Object {
                    (Get-ADGroup -Identity $_).GroupCategory -eq 'Security'
                }
            }

            # Get the groups for the Target user
            $targetGroups = Get-ADUser -Identity $Target -Property MemberOf | Select-Object -ExpandProperty MemberOf

            # Filter target groups based on type
            if ($Type -eq "DistributionOnly") {
                $targetGroups = $targetGroups | Where-Object {
                    (Get-ADGroup -Identity $_).GroupCategory -eq 'Distribution'
                }
            } elseif ($Type -eq "SecurityOnly") {
                $targetGroups = $targetGroups | Where-Object {
                    (Get-ADGroup -Identity $_).GroupCategory -eq 'Security'
                }
            }

            if ($Clone) {
                # Determine groups to remove and add
                $groupsToRemove = $targetGroups | Where-Object { $_ -notin $sourceGroups }
                $groupsToAdd = $sourceGroups | Where-Object { $_ -notin $targetGroups }

                # Remove Target from groups
                if ($groupsToRemove.Count -gt 0) {
                    Remove-ADGroupMember -Identity $groupsToRemove -Members $Target -Confirm:$false -ErrorAction Stop
                    Centralized-Log "Removed $Target from groups: $($groupsToRemove -join ', ')"
                }

                # Add Target to groups
                if ($groupsToAdd.Count -gt 0) {
                    Add-ADGroupMember -Identity $groupsToAdd -Members $Target -ErrorAction Stop
                    Centralized-Log "Added $Target to groups: $($groupsToAdd -join ', ')"
                }
            }

            if ($Add) {
                # Determine groups to add
                $groupsToAdd = $sourceGroups | Where-Object { $_ -notin $targetGroups }

                # Add Target to groups
                if ($groupsToAdd.Count -gt 0) {
                    Add-ADGroupMember -Identity $groupsToAdd -Members $Target -ErrorAction Stop
                    Centralized-Log "Added $Target to groups: $($groupsToAdd -join ', ')"
                }
            }

        } catch {
            Centralized-Log "An error occurred: $_"
            Write-Error "An error occurred: $_"
        }
    }
}

function Centralized-Log {
    param (
        [string]$message
    )

    $mutex = New-Object System.Threading.Mutex($false, "LogMutex")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "$timestamp : $message"

    try {
        $mutex.WaitOne() # Wait for the mutex to be free
        Add-Content -Path $script:logFilePath -Value $logEntry -Force
    } finally {
        $mutex.ReleaseMutex() # Release the mutex for other threads
    }

    if ($VerbosePreference -eq "Continue") {
        Write-Verbose $message
    }
}




function Centralized-Log2([string]$message) {
    $mutex = New-Object System.Threading.Mutex($false, "LogMutex")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "$timestamp : $message"

    try {
        $mutex.WaitOne() # Wait for the mutex to be free
        Add-Content -Path $logFilePath -Value $logEntry -Force
    } finally {
        $mutex.ReleaseMutex() # Release the mutex for other threads
    }
}

function Copy-DirectoryInBackground([string]$sourcePath, [string]$destinationPath) {
    while ((Get-Job -State Running).Count -ge 20) {
        Start-Sleep -Seconds 3 # Wait for 3 seconds before checking again
    }

    Start-Job -ScriptBlock {
        param($sourcePath, $destinationPath)
        try {
            Copy-Item -Path $sourcePath -Destination $destinationPath -Recurse
        } catch {
            Centralized-Log "Failed to copy file $($_.Exception.ItemName): $($_.Exception.Message)"
        }
    } -ArgumentList $sourcePath, $destinationPath
}