Public/Copy-ADTFileToUserProfiles.ps1

#-----------------------------------------------------------------------------
#
# MARK: Copy-ADTFileToUserProfiles
#
#-----------------------------------------------------------------------------

function Copy-ADTFileToUserProfiles
{
    <#
    .SYNOPSIS
        Copy one or more items to each user profile on the system.
 
    .DESCRIPTION
        The Copy-ADTFileToUserProfiles function copies one or more items to each user profile on the system. It supports various options such as recursion, flattening files, and using Robocopy to overcome the 260 character limit.
 
    .PARAMETER Path
        The path of the file or folder to copy.
 
    .PARAMETER Destination
        The path of the destination folder to append to the root of the user profile.
 
    .PARAMETER BasePath
        The base path to append the destination folder to. Default is: Profile. Options are: Profile, AppData, LocalAppData, Desktop, Documents, StartMenu, Temp, OneDrive, OneDriveCommercial.
 
    .PARAMETER Recurse
        Copy files in subdirectories.
 
    .PARAMETER Flatten
        Flattens the files into the root destination directory.
 
    .PARAMETER ContinueFileCopyOnError
        Continue copying files if an error is encountered. This will continue the deployment script and will warn about files that failed to be copied.
 
    .PARAMETER FileCopyMode
        Select from 'Native' or 'Robocopy'. Default is configured in config.psd1. Note that Robocopy supports * in file names, but not folders, in source paths.
 
    .PARAMETER RobocopyParams
        Override the default Robocopy parameters. Default is: /NJH /NJS /NS /NC /NP /NDL /FP /IS /IT /IM /XX /MT:4 /R:1 /W:1
 
    .PARAMETER RobocopyAdditionalParams
        Append to the default Robocopy parameters. Default is: /NJH /NJS /NS /NC /NP /NDL /FP /IS /IT /IM /XX /MT:4 /R:1 /W:1
 
    .PARAMETER ExcludeNTAccount
        Specify NT account names in Domain\Username format to exclude from the list of user profiles.
 
    .PARAMETER IncludeSystemProfiles
        Include system profiles: SYSTEM, LOCAL SERVICE, NETWORK SERVICE. Default is: $false.
 
    .PARAMETER IncludeServiceProfiles
        Include service profiles where NTAccount begins with NT SERVICE. Default is: $false.
 
    .PARAMETER ExcludeDefaultUser
        Exclude the Default User. Default is: $false.
 
    .INPUTS
        System.String[]
 
        You can pipe in string values for $Path.
 
    .OUTPUTS
        None
 
        This function does not generate any output.
 
    .EXAMPLE
        Copy-ADTFileToUserProfiles -Path "$dirSupportFiles\config.txt" -Destination "AppData\Roaming\MyApp"
 
        Copy a single file to C:\Users\<UserName>\AppData\Roaming\MyApp for each user.
 
    .EXAMPLE
        Copy-ADTFileToUserProfiles -Path "$dirSupportFiles\config.txt","$dirSupportFiles\config2.txt" -Destination "AppData\Roaming\MyApp"
 
        Copy two files to C:\Users\<UserName>\AppData\Roaming\MyApp for each user.
 
    .EXAMPLE
        Copy-ADTFileToUserProfiles -Path "$dirFiles\MyDocs" Destination "MyApp" -BasePath "Documents" -Recurse
 
        Copy an entire folder recursively to a new MyApp folder under each user's Documents folder.
 
    .EXAMPLE
        Copy-ADTFileToUserProfiles -Path "$dirFiles\.appConfigFolder" -Recurse
 
        Copy an entire folder to C:\Users\<UserName> for each user.
 
    .NOTES
        An active ADT session is NOT required to use this function.
 
        Tags: psadt
        Website: https://psappdeploytoolkit.com
        Copyright: (C) 2024 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
        License: https://opensource.org/license/lgpl-3-0
 
    .LINK
        https://psappdeploytoolkit.com
    #>


    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String[]]$Path,

        [Parameter(Mandatory = $false, Position = 2)]
        [ValidateNotNullOrEmpty()]
        [System.String]$Destination,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Profile', 'AppData', 'LocalAppData', 'Desktop', 'Documents', 'StartMenu', 'Temp', 'OneDrive', 'OneDriveCommercial')]
        [System.String]$BasePath = 'Profile',

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]$Recurse,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]$Flatten,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Native', 'Robocopy')]
        [System.String]$FileCopyMode,

        [Parameter(Mandatory = $false)]
        [System.String]$RobocopyParams,

        [Parameter(Mandatory = $false)]
        [System.String]$RobocopyAdditionalParams,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [System.String[]]$ExcludeNTAccount,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.SwitchParameter]$IncludeSystemProfiles,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.SwitchParameter]$IncludeServiceProfiles,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.SwitchParameter]$ExcludeDefaultUser,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.SwitchParameter]$ContinueFileCopyOnError
    )

    begin
    {
        # Initalize function.
        Initialize-ADTFunction -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        # Define default params for Copy-ADTFile.
        $CopyFileSplat = @{
            Recurse = $Recurse
            Flatten = $Flatten
            ContinueFileCopyOnError = $ContinueFileCopyOnError
        }
        if ($PSBoundParameters.ContainsKey('FileCopyMode'))
        {
            $CopyFileSplat.FileCopyMode = $PSBoundParameters.FileCopyMode
        }
        if ($PSBoundParameters.ContainsKey('RobocopyParams'))
        {
            $CopyFileSplat.RobocopyParams = $PSBoundParameters.RobocopyParams
        }
        if ($PSBoundParameters.ContainsKey('RobocopyAdditionalParams'))
        {
            $CopyFileSplat.RobocopyAdditionalParams = $PSBoundParameters.RobocopyAdditionalParams
        }
        if ($PSBoundParameters.ContainsKey('ErrorAction'))
        {
            $CopyFileSplat.ErrorAction = $PSBoundParameters.ErrorAction
        }

        # Define default params for Get-ADTUserProfiles.
        $GetUserProfileSplat = @{
            IncludeSystemProfiles = $IncludeSystemProfiles
            IncludeServiceProfiles = $IncludeServiceProfiles
            ExcludeDefaultUser = $ExcludeDefaultUser
        }
        if ($ExcludeNTAccount)
        {
            $GetUserProfileSplat.ExcludeNTAccount = $ExcludeNTAccount
        }
        if ($BasePath -ne 'ProfilePath')
        {
            $GetUserProfileSplat.LoadProfilePaths = $true
        }

        # Collector for all provided paths.
        $sourcePaths = [System.Collections.Specialized.StringCollection]::new()
    }

    process
    {
        # Add all source paths to the collection.
        $sourcePaths.AddRange($Path)
    }

    end
    {
        # Copy all paths to the specified destination.
        foreach ($UserProfile in (Get-ADTUserProfiles @GetUserProfileSplat))
        {
            if ([string]::IsNullOrWhiteSpace($UserProfile."$BasePath`Path"))
            {
                Write-ADTLogEntry -Message "Skipping user profile [$($UserProfile.NTAccount)] as path [$BasePath`Path] is not available."
                continue
            }
            $dest = Join-Path $UserProfile."$BasePath`Path" $Destination
            Write-ADTLogEntry -Message "Copying path [$Path] to $($dest):"
            Copy-ADTFile -Path $sourcePaths -Destination $dest @CopyFileSplat
        }

        # Finalize function.
        Complete-ADTFunction -Cmdlet $PSCmdlet
    }
}