Functions/Copy-AxiumFiles.ps1
function Copy-AxiumFiles { <# .SYNOPSIS Used to copy axiUm files. Usually run as a startup or logon script. .DESCRIPTION Used to copy axiUm files. Usually run as a startup or logon script. Can be limited to only copy files when connected to a given network, not connected to a given network, or both. This is useful to copy files when connected to your organization's network, but not through on offsite VPN connection which may potentially be slow. Uses RoboCopy under the hood, so it requires RoboCopy to be located in the path, which is the case by default in Windows Vista and latter. RoboCopy will log to "$ClientPath\Copy-AxiumFiles.log". Aliases: cpaf .INPUTS System.String .EXAMPLE PS> 'C:\axiUm' | Copy-AxiumFiles -SourcePathOrPrefix $PSScriptRoot Copies the files for a single instance of axiUm from the directory this script is in to "C:\axiUm", writing a log file to "C:\axiUm\Copy-AxiumFiles.log". Only new and updated files are copied, and the IP addresses of the device are not considered. .EXAMPLE PS> 'C:\axiUm' | Copy-AxiumFiles -SourcePathOrPrefix $PSScriptRoot -CopyAll Same as Example 1, but copies all files. .EXAMPLE PS> 'C:\axiUm' | Copy-AxiumFiles -SourcePathOrPrefix $PSScriptRoot -RequireInSubnet @('10.0.0.0', '255.0.0.0') -RequireNotInSubnet @('10.2.0.0', '255.255.0.0) Let us say that your organization has IP addresses in 10.0.0.0/255.0.0.0, but your VPN uses a small part of that (10.1.0.0/255.255.0.0). This would do the same as Example 1, but only if the workstation this was run on was connected to your organization's network, but not through VPN. This is useful if you want to push out a lot of files, but are worried about doing this when somebody is connected via a slow offsite wifi connection to your VPN. This would skip those workstations, which you could then handle manually. .EXAMPLE PS> 'C:\axiUm' | Get-ChildItem -Directory | Copy-AxiumFiles -SourcePathOrPrefix '\\domain\axiUm-ClientFiles-' -MultipleCopies This is an example of using the MultipleCopies switch to copy files for multiple copies of axiUm. Let us assume there are two installations of axiUm on the workstation this is being run on: * "C:\axiUm\Production" * "C:\axiUm\Test" This will: * Copy all files that are not in or newer than the ones in "C:\axiUm\Production" from "\\domain\axiUm-ClientFiles-Production" to "C:\axiUm\Production". * Copy all files that are not in or newer than the ones in "C:\axiUm\Test" from "\\domain\axiUm-ClientFiles-Test" to "C:\axiUm\Test". .NOTES Author : Dan Thompson Copyright : 2020 Case Western Reserve University #> [CmdletBinding(SupportsShouldProcess)] param( # Where axiUm is installed. # # Aliases: clp [Parameter( Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, Mandatory = $True )] [ValidateNotNullOrEmpty()] [ValidateScript({ $_ | Test-Path -PathType 'Container' })] [Alias('clp')] [string]$ClientPath, # If MultipleCopies is set, the name of the last folder in ClientPath will be appended to # SourcePathOrPrefix to get where to copy files from. Otherwise, SourcePathOrPrefix will be treated as the # complete path of where the files should be copied from. # # Aliases: spp [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [Alias('spp')] [string]$SourcePathOrPrefix, # An array of 2 IP addresses, the first being the subnet, and the second the subnet mask. Files will only # be copied if an IP address IS found connected to this subnet. If not set, this will not be used to # determine if files should be copied. # # Aliases: ris, insubnet, in_subnet [ValidateCount(2,2)] [Alias('ris', 'insubnet', 'in_subnet')] [System.Net.IPAddress[]]$RequireInSubnet, # An array of 2 IP addresses, the first being the subnet, and the second the subnet mask. Files will only # be copied if an IP address IS NOT found connected to this subnet. If not set, this will not be used to # determine if files should be copied. # # Aliases: rnis, notinsubnet, not_in_subnet [ValidateCount(2,2)] [Alias('rnis', 'notinsubnet', 'not_in_subnet')] [System.Net.IPAddress[]]$RequireNotInSubnet, # By default, the only files copied are those that are only in the source, or have been modified in the # source since they were last copied. This switch changes that behavior to copy ALL files every time this # script is run. # # Aliases: cpa [Alias('cpa')] [switch]$CopyAll, # Set this if the computer(s) you are running this on may have more than copy installed. This might be the # case if you have a test copy of axiUm that is on a different version from your production instance, thus # requiring a separate install. # # Aliases: mi [Alias('mi')] [switch]$MultipleCopies, # By default, log files will be cleared before being written to. This changes that behavior to just append # to the existing log. Use this carefully, as, in certain circumstances, you could get log files that grow # to a very large size (such as if this function is called in a logon or startup script, and you do not # have another method in place for clearing the log files). # # Aliases: al [Alias('al')] [switch]$AppendToLog ) begin { # Abort if RoboCopy isn't in the path. if ($Null -eq (Get-Command -Name 'robocopy' -ErrorAction SilentlyContinue)) { throw 'RoboCopy was not found in your path. Aborting.' } # Determine if we meet the requirements set forth to copy files. $SubnetRequirements = @{} if ($PSBoundParameters.ContainsKey('RequireInSubnet') -and ($Null -ne $RequireInSubnet)) { $SubnetRequirements.InSubnet = $RequireInSubnet } if ($PSBoundParameters.ContainsKey('RequireNotInSubnet') -and ($Null -ne $RequireNotInSubnet)) { $SubnetRequirements.NotInSubnet = $RequireNotInSubnet } $CanCopy = (Get-IPAddresses | Test-IPAddressRequirementsMet @SubnetRequirements).Contains($True) if ($CanCopy) { Write-Verbose -Message 'Met requirements to copy files.' } else { Write-Warning -Message 'Requirements to copy files not met, so not doing anything. Bye now!' } } process { if ($CanCopy) { # Get the actual source path. This will be different from $SourcePathOrPrefix if we have multiple copies # of axiUm. $SourcePath = $SourcePathOrPrefix if ($MultipleCopies.IsPresent) { $SourcePath = $SourcePathOrPrefix | Join-Path -ChildPath ($ClientPath | Split-Path -Leaf) } # Check if we have source files for the copy of axiUm. $HaveSourceFiles = $True if ($MultipleCopies.IsPresent) { $HaveSourceFiles = $SourcePath | Test-Path -PathType 'Container' } if ($HaveSourceFiles) { # We do, so we are good to copy the files. Write-Verbose -Message """$SourcePath"" exists, so copying contents to ""$ClientPath"" ..." # Set up some RoboCopy options. We have to do this here as doing it in begin will cause # $RobocopyOptions to not get emptied for each copy of axiUm. $RobocopyOptions = @('/E') if (-not $CopyAll.IsPresent) { $RobocopyOptions += '/XO' } $LogPath = $ClientPath | Join-Path -ChildPath 'Copy-AxiumFiles.log' $RobocopyLogFlag = '/UNILOG' if ($AppendToLog.IsPresent) { $RobocopyLogFlag += '+' } $RobocopyLogFlag += ":$LogPath" $RobocopyOptions += $RobocopyLogFlag # Call RoboCopy. $RobocopyArgs = @($SourcePath, $ClientPath) + $RobocopyOptions if ($PSCmdlet.ShouldProcess('robocopy', 'Start-Process')) { $RunMessageSuffix = "robocopy $RobocopyArgs" Write-Verbose -Message "Attempting to run: $RunMessageSuffix" $RobocopyProcess = Start-Process -FilePath 'robocopy' -ArgumentList $RobocopyArgs -Wait -PassThru $ExitCodeMessage = "Exit code was $($RobocopyProcess.ExitCode). See https://docs.microsoft.com/en-us/troubleshoot/windows-server/backup-and-storage/return-codes-used-robocopy-utility for details." if ($RobocopyProcess.ExitCode -gt 7) { Write-Error -Message "Encountered error when running: $RunMessageSuffix" Write-Error -Message $ExitCodeMessage $False } else { Write-Verbose -Message "Successfully ran: $RunMessageSuffix" Write-Verbose -Message $ExitCodeMessage $True } } else { $True } } else { # We don't. Write-Warning -Message "Directory ""$SourcePath"" doesn't exist. Not copying contents to ""$ClientPath""." } } } } |