WinDirOps.psm1

#Region './Classes/FolderDeletionResult.ps1' -1

class FolderDeletionResult {
    [string]$FolderPath
    [bool]$DeletionSuccess
    [string]$DeletionMessage
    [string]$ComputerName

    # Constructor to initialize the properties
    FolderDeletionResult([string]$folderPath, [bool]$deletionSuccess, [string]$deletionMessage, [string]$computerName) {
        $this.FolderPath = $folderPath
        $this.DeletionSuccess = $deletionSuccess
        $this.DeletionMessage = $deletionMessage
        $this.ComputerName = $computerName
    }
}
#EndRegion './Classes/FolderDeletionResult.ps1' 15
#Region './Public/Clear-Directory.ps1' -1

<#
.SYNOPSIS
    Deletes a specified directory on the local or remote computer.

.DESCRIPTION
    This function attempts to delete an entire directory and its contents either on the local or a remote computer.
    It determines whether the operation is local or remote using the `ComputerName` parameter.
    Internally, it delegates to `Remove-DirectoryByType` to handle the deletion, making this a high-level function
    for directory deletion on any machine.

.PARAMETER Directory
    The path of the directory to be deleted.

.PARAMETER ComputerName
    The name of the computer where the directory resides. Defaults to the local computer.

.EXAMPLE
    Clear-Directory -Directory "C:\Temp\Logs"

    This example deletes the "C:\Temp\Logs" directory on the local machine.

.EXAMPLE
    Clear-Directory -Directory "D:\OldFiles" -ComputerName "RemotePC"

    This example deletes the "D:\OldFiles" directory on a remote computer named "RemotePC".

.NOTES
    This function determines whether the operation is local or remote and uses the appropriate
    function (either `Remove-LocalDirectory` or `Remove-RemoteDirectory`) to perform the deletion.
    It supports `-WhatIf` and `-Confirm` for previewing or confirming the operation.
#>


function Clear-Directory
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [string]$Directory,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$ComputerName = $env:COMPUTERNAME  # Default to local computer
    )

    process
    {
        if ($PSCmdlet.ShouldProcess($Directory, "Delete directory on $ComputerName"))
        {
            $result = Remove-DirectoryByType -Directory $Directory -ComputerName $ComputerName -Confirm:$false
            Write-Information -MessageData $result.DeletionMessage -Tags "DeleteOperation"
            return $result
        }
    }
}
#EndRegion './Public/Clear-Directory.ps1' 54
#Region './Public/Clear-DirectoryContents.ps1' -1

<#
.SYNOPSIS
    Clears only the contents of a directory without deleting the directory itself.

.DESCRIPTION
    This function clears the contents of a directory by mirroring it with an empty temporary directory.
    It does not delete the directory itself but removes all files and subfolders within the specified directory.
    It uses `Invoke-MirRoboCopy` to sync an empty directory with the target directory, effectively clearing it.

.PARAMETER Directory
    The path of the directory whose contents will be cleared.

.EXAMPLE
    Clear-DirectoryContents -Directory "C:\Temp\OldData"

    Clears the contents in the "C:\Temp\OldData" directory but does not remove the directory itself.

.NOTES
    This function is for clearing the contents of a directory while preserving the directory itself.
    To delete both the directory and its contents, use `Clear-Directory` or `Remove-DirectoryByType`.
#>


function Clear-DirectoryContents
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Directory
    )

    try
    {
        # Create a temporary empty directory
        $emptyDir = [System.IO.Path]::GetTempPath() + [System.Guid]::NewGuid().ToString()
        mkdir $emptyDir | Out-Null

        if ($PSCmdlet.ShouldProcess($Directory, "Clear directory contents"))
        {
            # Use Invoke-MirRoboCopy to fast-delete the directory contents by syncing an empty directory
            $syncSuccess = Invoke-MirRoboCopy -SourceDirectory $emptyDir -TargetDirectory $Directory -confirm:$false

            if ($syncSuccess)
            {
                # Remove the target directory after clearing its contents
                Remove-Item -Path $Directory -Recurse -Force -ErrorAction SilentlyContinue
                Remove-Item -Path $emptyDir -Recurse -Force -ErrorAction SilentlyContinue

                return $true
            }
            else
            {
                return $false
            }
        }
    }
    catch
    {
        Write-Error "Failed to clear directory contents for $Directory. Error: $_"
        return $false
    }
}
#EndRegion './Public/Clear-DirectoryContents.ps1' 62
#Region './Public/Invoke-MirRoboCopy.ps1' -1

<#
.SYNOPSIS
    Mirrors the contents of a source directory to a target directory using RoboCopy.

.DESCRIPTION
    This function mirrors the contents of a source directory to a target directory using the RoboCopy utility.
    It ensures that the contents of the target directory exactly match the source directory by syncing them.

.PARAMETER SourceDirectory
    The path of the source directory whose contents will be mirrored to the target directory.

.PARAMETER TargetDirectory
    The path of the target directory where the source directory contents will be mirrored.

.EXAMPLE
    Invoke-MirRoboCopy -SourceDirectory "C:\SourceFolder" -TargetDirectory "C:\TargetFolder"

    Mirrors the contents of "C:\SourceFolder" to "C:\TargetFolder".

.NOTES
    This function is a low-level utility used by other functions like `Clear-DirectoryContents`
    to handle the mirroring operation.

    RoboCopy exit codes:
    0 = No files were copied
    1 = One or more files were copied successfully (even if some files were skipped)
    8 = Some files or directories could not be copied (failure)
    16 = Fatal error (failure)

    Exit codes 8 and higher are considered failures in this function.
#>

function Invoke-MirRoboCopy
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true)]
        [string]$SourceDirectory,

        [Parameter(Mandatory = $true)]
        [string]$TargetDirectory
    )

    try
    {
        if ($PSCmdlet.ShouldProcess($TargetDirectory, "Mirror directory from '$SourceDirectory'"))
        {
            # Use robocopy to mirror the source directory to the target directory
            $result = robocopy $SourceDirectory $TargetDirectory /mir

            # Check robocopy result
            if ($result -ge 8)
            {
                throw "RoboCopy encountered a failure with code $result."
            }

            return $true
        }
    }
    catch
    {
        Write-Error "RoboCopy failed from '$SourceDirectory' to '$TargetDirectory'. Error: $_"
        return $false
    }
}
#EndRegion './Public/Invoke-MirRoboCopy.ps1' 65
#Region './Public/Remove-DirectoryByType.ps1' -1

<#
.SYNOPSIS
    Deletes a directory either locally or remotely based on the computer name provided.

.DESCRIPTION
    This function deletes a directory either on the local machine or a remote machine depending on
    the computer name provided. It determines whether the operation is local or remote and calls the
    appropriate function (either `Remove-LocalDirectory` or `Remove-RemoteDirectory`).
    It supports `-WhatIf` and `-Confirm` to allow users to preview or confirm the deletion before it is performed.

.PARAMETER Directory
    The path of the directory to be deleted.

.PARAMETER ComputerName
    The name of the computer where the directory resides. Defaults to the local computer.

.EXAMPLE
    Remove-DirectoryByType -Directory "C:\Temp\Logs"

    Deletes the directory "C:\Temp\Logs" from the local machine.

.NOTES
    This function determines whether the directory is on the local or remote computer and uses
    the appropriate method to delete it.
#>


function Remove-DirectoryByType
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Directory,

        [Parameter(Mandatory = $false)]
        [string]$ComputerName = $env:COMPUTERNAME
    )

    $isLocal = $ComputerName -eq $env:COMPUTERNAME

    if ($PSCmdlet.ShouldProcess("$ComputerName': $Directory", "Delete directory by type"))
    {
        if ($isLocal)
        {
            return Remove-LocalDirectory -Directory $Directory -confirm:$false
        }
        else
        {
            return Remove-RemoteDirectory -Directory $Directory -ComputerName $ComputerName -confirm:$false
        }
    }
}
#EndRegion './Public/Remove-DirectoryByType.ps1' 52
#Region './Public/Remove-LocalDirectory.ps1' -1

<#
.SYNOPSIS
    Deletes a local directory and its contents.

.DESCRIPTION
    This function deletes a specified directory on the local computer.
    It first clears the contents of the directory and then removes the directory itself.

.PARAMETER Directory
    The path of the local directory to be deleted.

.EXAMPLE
    Remove-LocalDirectory -Directory "C:\Temp\OldData"

    Deletes the "C:\Temp\OldData" directory and its contents on the local machine.

.NOTES
    This function works only on local directories. For remote directories, use `Remove-RemoteDirectory`.
#>



function Remove-LocalDirectory
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Directory
    )

    try
    {
        if ($PSCmdlet.ShouldProcess($Directory, "Delete local directory"))
        {
            if (Clear-DirectoryContents -Directory $Directory -Confirm:$false)
            {
                return [FolderDeletionResult]::new(
                    $Directory,
                    $true,
                    "Successfully deleted local directory '$Directory'.",
                    $env:COMPUTERNAME
                )
            }
            else
            {
                return [FolderDeletionResult]::new(
                    $Directory,
                    $false,
                    "Failed to delete local directory '$Directory'.",
                    $env:COMPUTERNAME
                )
            }
        }
    }
    catch
    {
        return [FolderDeletionResult]::new(
            $Directory,
            $false,
            "Error occurred while deleting local directory '$Directory'. $_",
            $env:COMPUTERNAME
        )
    }
}
#EndRegion './Public/Remove-LocalDirectory.ps1' 64
#Region './Public/Remove-RemoteDirectory.ps1' -1

<#
.SYNOPSIS
    Deletes a remote directory and its contents.

.DESCRIPTION
    This function deletes a directory and its contents on a remote computer.
    It sends a script block to the remote computer to clear the directory contents using `Clear-DirectoryContents`,
    then deletes the directory itself.

.PARAMETER Directory
    The path of the remote directory to be deleted.

.PARAMETER ComputerName
    The name of the remote computer where the directory is located.

.EXAMPLE
    Remove-RemoteDirectory -Directory "D:\OldFiles" -ComputerName "RemotePC"

    Deletes the "D:\OldFiles" directory and its contents on the remote computer "RemotePC".

.NOTES
    This function is specifically for remote directories.
    Use `Remove-LocalDirectory` for local directory deletions.
#>


function Remove-RemoteDirectory
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Directory,

        [Parameter(Mandatory = $true)]
        [string]$ComputerName
    )


    # Check if the remote computer is online
    if (-not (Test-ComputerPing -ComputerName $ComputerName))
    {
        # Return FolderDeletionResult indicating the computer is offline
        return [FolderDeletionResult]::new(
            $Directory,
            $false,
            "The remote computer '$ComputerName' is offline or unreachable.",
            $ComputerName
        )
    }


    # Get the full script block for Clear-DirectoryContents function
    $clearDirectoryFunction = Get-FunctionScriptBlock -FunctionName 'Clear-DirectoryContents'
    $IvokeMirRoboCopyFunction = Get-FunctionScriptBlock -FunctionName 'Invoke-MirRoboCopy'

    if (-not $clearDirectoryFunction -or -not $IvokeMirRoboCopyFunction)
    {
        Write-Error "Unable to retrieve Clear-DirectoryContents function."
        return
    }

    $scriptBlock = {
        param ($remoteDirectory, $clearFunction, $RoboCopyFunction)

        # Define the function from the passed string
        Invoke-Expression $clearFunction, $RoboCopyFunction

        # Call the Clear-DirectoryContents function
        if (Clear-DirectoryContents -Directory $remoteDirectory -confirm:$false)
        {
            return @{
                Directory    = $remoteDirectory
                Success      = $true
                Message      = "Successfully deleted remote directory '$remoteDirectory'."
                ComputerName = $env:COMPUTERNAME
            }
        }
        else
        {
            return @{
                Directory    = $remoteDirectory
                Success      = $false
                Message      = "Failed to delete remote directory '$remoteDirectory'."
                ComputerName = $env:COMPUTERNAME
            }
        }
    }

    try
    {
        if ($PSCmdlet.ShouldProcess("$ComputerName`: $Directory", "Delete remote directory"))
        {
            # Execute the script block on the remote machine, passing the function string and directory
            $rawResult = Invoke-Command -ComputerName $ComputerName -ScriptBlock $scriptBlock -ArgumentList $Directory, $clearDirectoryFunction, $invokeMirRoboCopyFunction

            # Construct the FolderDeletionResult object locally
            return [FolderDeletionResult]::new(
                $rawResult.Directory,
                $rawResult.Success,
                $rawResult.Message,
                $rawResult.ComputerName
            )
        }
    }
    catch
    {
        return [FolderDeletionResult]::new(
            $Directory,
            $false,
            "Error occurred while deleting remote directory '$Directory'. $_",
            $ComputerName
        )
    }
}
#EndRegion './Public/Remove-RemoteDirectory.ps1' 114