EnhancedDeviceMigrationAO.psm1

#Region '.\Public\Add-LocalUser.ps1' -1

function Add-LocalUser {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$TempUser,

        [Parameter(Mandatory = $true)]
        [string]$TempUserPassword,

        [Parameter(Mandatory = $true)]
        [string]$Description,

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

    Begin {
        Write-EnhancedLog -Message "Starting Add-LocalUser function" -Level "INFO"
        Log-Params -Params @{ 
            TempUser         = $TempUser
            TempUserPassword = $TempUserPassword
            Description      = $Description
            Group            = $Group
        }
    }

    Process {
        try {
            Write-EnhancedLog -Message "Creating Local User Account" -Level "INFO"
            $Password = ConvertTo-SecureString -AsPlainText $TempUserPassword -Force
            New-LocalUser -Name $TempUser -Password $Password -Description $Description -AccountNeverExpires
            Add-LocalGroupMember -Group $Group -Member $TempUser
        } catch {
            Write-EnhancedLog -Message "An error occurred while adding local user: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Add-LocalUser function" -Level "INFO"
    }
}

# # Define parameters
# $AddLocalUserParams = @{
# TempUser = "YourTempUser"
# TempUserPassword = "YourTempUserPassword"
# Description = "account for autologin"
# Group = "Administrators"
# }

# # Example usage with splatting
# Add-LocalUser @AddLocalUserParams
#EndRegion '.\Public\Add-LocalUser.ps1' 54
#Region '.\Public\Backup-ChromeBookmarkstoOneDrive.ps1' -1

function Backup-ChromeBookmarksToOneDrive {
    <#
    .SYNOPSIS
    Backs up the Chrome bookmarks to OneDrive.
 
    .DESCRIPTION
    This function copies the Chrome bookmarks file from the Chrome user profile to a specified OneDrive backup directory using Robocopy. It verifies the existence of the OneDrive directory and uses logging for the backup process.
 
    .PARAMETER ChromeProfilePath
    The path to the Chrome user profile directory.
 
    .PARAMETER BackupFolderName
    The name of the backup folder within OneDrive.
 
    .PARAMETER Exclude
    The directories or files to exclude from the copy operation. Default is ".git".
 
    .PARAMETER RetryCount
    The number of retries if a copy fails. Default is 2.
 
    .PARAMETER WaitTime
    The wait time between retries in seconds. Default is 5.
 
    .PARAMETER RequiredSpaceGB
    The required free space in gigabytes at the destination. Default is 10 GB.
 
    .EXAMPLE
    $params = @{
        ChromeProfilePath = "$env:USERPROFILE\AppData\Local\Google\Chrome\User Data\Default"
        BackupFolderName = "ChromeBackup"
        Exclude = ".git"
        RetryCount = 2
        WaitTime = 5
        RequiredSpaceGB = 10
    }
    Backup-ChromeBookmarksToOneDrive @params
    #>


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

        [Parameter(Mandatory = $true)]
        [string]$BackupFolderName,

        [Parameter(Mandatory = $false)]
        [string[]]$Exclude = ".git",

        [Parameter(Mandatory = $false)]
        [int]$RetryCount = 2,

        [Parameter(Mandatory = $false)]
        [int]$WaitTime = 5,

        [Parameter(Mandatory = $false)]
        [int]$RequiredSpaceGB = 10
    )

    Begin {
        Write-EnhancedLog -Message "Starting Backup-ChromeBookmarksToOneDrive function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Check for OneDrive directory existence
            $oneDriveDirectory = (Get-ChildItem -Path "$env:USERPROFILE" -Filter "OneDrive - *" -Directory).FullName
            if (-not $oneDriveDirectory) {
                Throw "OneDrive directory not found. Please ensure OneDrive is set up correctly."
            }
            Write-EnhancedLog -Message "OneDrive directory found: $oneDriveDirectory" -Level "INFO"

            # Check if the Chrome profile directory exists
            if (-not (Test-Path -Path $ChromeProfilePath)) {
                Write-EnhancedLog -Message "Chrome profile directory not found. It seems Google Chrome is not installed or used." -Level "Warning"
                return
            }
            Write-EnhancedLog -Message "Chrome profile directory found: $ChromeProfilePath" -Level "INFO"

            # Define the destination path within the OneDrive directory
            $backupPath = Join-Path -Path $oneDriveDirectory -ChildPath $BackupFolderName

            # Check if destination path exists, if not create it
            if (-not (Test-Path -Path $backupPath)) {
                New-Item -Path $backupPath -ItemType Directory -Force | Out-Null
                Write-EnhancedLog -Message "Created backup directory at: $backupPath" -Level "INFO"
            }

            # Temporary directory for copying the specific file
            $tempDir = "C:\Users\Admin-Abdullah\AppData\Local\Temp\ChromeTemp"

            # Ensure the temporary directory exists
            if (-not (Test-Path -Path $tempDir)) {
                New-Item -ItemType Directory -Path $tempDir
            }

            # Files to back up
            $filesToBackup = @("Bookmarks")

            foreach ($file in $filesToBackup) {
                $sourceFilePath = $ChromeProfilePath # Robocopy uses directory paths
                $destinationFilePath = $backupPath # Robocopy destination directory

                # Check if the source file exists before attempting to copy
                if (Test-Path -Path (Join-Path -Path $sourceFilePath -ChildPath $file)) {
                    try {
                        # Copy the Bookmarks file to the temporary directory
                        Copy-Item -Path (Join-Path -Path $sourceFilePath -ChildPath $file) -Destination $tempDir

                        # Use splatting for function parameters
                        $params = @{
                            Source          = $tempDir
                            Destination     = $destinationFilePath
                            FilePattern     = $file
                            Exclude         = $Exclude
                            RetryCount      = $RetryCount
                            WaitTime        = $WaitTime
                            RequiredSpaceGB = $RequiredSpaceGB
                        }
                        
                        # Execute the function with splatting
                        Copy-FilesWithRobocopy @params

                        Write-EnhancedLog -Message "Successfully backed up '$file' to '$destinationFilePath'." -Level "INFO"

                        # Remove the temporary directory
                        Remove-Item -Path $tempDir -Recurse -Force
                    }
                    catch {
                        Write-EnhancedLog -Message "An error occurred while backing up '$file': $_" -Level "ERROR"
                        Handle-Error -ErrorRecord $_
                    }
                }
                else {
                    Write-EnhancedLog -Message "'$file' does not exist in the source directory and will not be backed up." -Level "Warning"
                }
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Backup-ChromeBookmarksToOneDrive function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Backup-ChromeBookmarksToOneDrive function" -Level "Notice"
    }
}

# # Example usage
# $params = @{
# ChromeProfilePath = "$env:USERPROFILE\AppData\Local\Google\Chrome\User Data\Default"
# BackupFolderName = "ChromeBackup"
# Exclude = ".git"
# RetryCount = 2
# WaitTime = 5
# RequiredSpaceGB = 10
# }
# Backup-ChromeBookmarksToOneDrive @params
#EndRegion '.\Public\Backup-ChromeBookmarkstoOneDrive.ps1' 161
#Region '.\Public\Backup-DownloadsToOneDrive.ps1' -1

function Backup-DownloadsToOneDrive {
    <#
    .SYNOPSIS
    Backs up the Downloads folder to OneDrive.
 
    .DESCRIPTION
    This function copies all files from the Downloads folder to a specified OneDrive backup directory using Robocopy. It verifies the existence of the OneDrive directory and uses logging for the backup process.
 
    .PARAMETER DownloadsPath
    The path to the Downloads folder.
 
    .PARAMETER BackupFolderName
    The name of the backup folder within OneDrive.
 
    .PARAMETER Exclude
    The directories or files to exclude from the copy operation. Default is ".git".
 
    .PARAMETER RetryCount
    The number of retries if a copy fails. Default is 2.
 
    .PARAMETER WaitTime
    The wait time between retries in seconds. Default is 5.
 
    .PARAMETER RequiredSpaceGB
    The required free space in gigabytes at the destination. Default is 10 GB.
 
    .EXAMPLE
    $params = @{
        DownloadsPath = "$env:USERPROFILE\Downloads"
        BackupFolderName = "DownloadsBackup"
        Exclude = ".git"
        RetryCount = 2
        WaitTime = 5
        RequiredSpaceGB = 10
    }
    Backup-DownloadsToOneDrive @params
    #>


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

        [Parameter(Mandatory = $true)]
        [string]$BackupFolderName,

        [Parameter(Mandatory = $false)]
        [string[]]$Exclude = ".git",

        [Parameter(Mandatory = $false)]
        [int]$RetryCount = 2,

        [Parameter(Mandatory = $false)]
        [int]$WaitTime = 5,

        [Parameter(Mandatory = $false)]
        [int]$RequiredSpaceGB = 10
    )

    Begin {
        Write-EnhancedLog -Message "Starting Backup-DownloadsToOneDrive function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Check for OneDrive directory existence
            $oneDriveDirectory = (Get-ChildItem -Path "$env:USERPROFILE" -Filter "OneDrive - *" -Directory).FullName
            if (-not $oneDriveDirectory) {
                Throw "OneDrive directory not found. Please ensure OneDrive is set up correctly."
            }
            Write-EnhancedLog -Message "OneDrive directory found: $oneDriveDirectory" -Level "INFO"

            # Check if the Downloads directory exists
            if (-not (Test-Path -Path $DownloadsPath)) {
                Throw "Downloads directory not found. Please ensure the path is correct."
            }
            Write-EnhancedLog -Message "Downloads directory found: $DownloadsPath" -Level "INFO"

            # Define the destination path within the OneDrive directory
            $backupPath = Join-Path -Path $oneDriveDirectory -ChildPath $BackupFolderName

            # Check if destination path exists, if not create it
            if (-not (Test-Path -Path $backupPath)) {
                New-Item -Path $backupPath -ItemType Directory -Force | Out-Null
                Write-EnhancedLog -Message "Created backup directory at: $backupPath" -Level "INFO"
            }

            # Use splatting for function parameters
            $params = @{
                Source          = $DownloadsPath
                Destination     = $backupPath
                FilePattern     = '*'
                Exclude         = $Exclude
                RetryCount      = $RetryCount
                WaitTime        = $WaitTime
                RequiredSpaceGB = $RequiredSpaceGB
            }

            # Execute the function with splatting
            Copy-FilesWithRobocopy @params

            Write-EnhancedLog -Message "Backup of Downloads to OneDrive completed successfully." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Backup-DownloadsToOneDrive function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Backup-DownloadsToOneDrive function" -Level "Notice"
    }
}

# Example usage
# $params = @{
# DownloadsPath = "$env:USERPROFILE\Downloads"
# BackupFolderName = "DownloadsBackup"
# Exclude = ".git"
# RetryCount = 2
# WaitTime = 5
# RequiredSpaceGB = 10
# }
# Backup-DownloadsToOneDrive @params
#EndRegion '.\Public\Backup-DownloadsToOneDrive.ps1' 126
#Region '.\Public\Backup-OutlookSignaturestoOneDrive.ps1' -1

function Backup-OutlookSignaturesToOneDrive {
    <#
    .SYNOPSIS
    Backs up the Outlook Signatures folder to OneDrive.
 
    .DESCRIPTION
    This function copies all files from the Outlook Signatures folder to a specified OneDrive backup directory using Robocopy. It verifies the existence of the OneDrive directory and uses logging for the backup process.
 
    .PARAMETER SignaturePath
    The path to the Outlook Signatures folder.
 
    .PARAMETER BackupFolderName
    The name of the backup folder within OneDrive.
 
    .PARAMETER Exclude
    The directories or files to exclude from the copy operation. Default is ".git".
 
    .PARAMETER RetryCount
    The number of retries if a copy fails. Default is 2.
 
    .PARAMETER WaitTime
    The wait time between retries in seconds. Default is 5.
 
    .PARAMETER RequiredSpaceGB
    The required free space in gigabytes at the destination. Default is 10 GB.
 
    .EXAMPLE
    $params = @{
        SignaturePath = "$env:USERPROFILE\AppData\Roaming\Microsoft\Signatures"
        BackupFolderName = "OutlookSignatures"
        Exclude = ".git"
        RetryCount = 2
        WaitTime = 5
        RequiredSpaceGB = 10
    }
    Backup-OutlookSignaturesToOneDrive @params
    #>


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

        [Parameter(Mandatory = $true)]
        [string]$BackupFolderName,

        [Parameter(Mandatory = $false)]
        [string[]]$Exclude = ".git",

        [Parameter(Mandatory = $false)]
        [int]$RetryCount = 2,

        [Parameter(Mandatory = $false)]
        [int]$WaitTime = 5,

        [Parameter(Mandatory = $false)]
        [int]$RequiredSpaceGB = 10
    )

    Begin {
        Write-EnhancedLog -Message "Starting Backup-OutlookSignaturesToOneDrive function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Check for OneDrive directory existence
            $oneDriveDirectory = (Get-ChildItem -Path "$env:USERPROFILE" -Filter "OneDrive - *" -Directory).FullName
            if (-not $oneDriveDirectory) {
                Throw "OneDrive directory not found. Please ensure OneDrive is set up correctly."
            }
            Write-EnhancedLog -Message "OneDrive directory found: $oneDriveDirectory" -Level "INFO"

            # Check if the Outlook Signatures directory exists
            if (-not (Test-Path -Path $SignaturePath)) {
                Write-EnhancedLog -Message "Outlook Signatures directory not found. It seems Outlook is not set up." -Level "Warning"
                return
            }
            Write-EnhancedLog -Message "Outlook Signatures directory found: $SignaturePath" -Level "INFO"

            # Define the destination path within the OneDrive directory
            $backupPath = Join-Path -Path $oneDriveDirectory -ChildPath $BackupFolderName

            # Check if destination path exists, if not create it
            if (-not (Test-Path -Path $backupPath)) {
                New-Item -Path $backupPath -ItemType Directory -Force | Out-Null
                Write-EnhancedLog -Message "Created backup directory at: $backupPath" -Level "INFO"
            }

            # Use splatting for function parameters
            $params = @{
                Source          = $SignaturePath
                Destination     = $backupPath
                FilePattern     = '*'
                Exclude         = $Exclude
                RetryCount      = $RetryCount
                WaitTime        = $WaitTime
                RequiredSpaceGB = $RequiredSpaceGB
            }

            # Execute the function with splatting
            Copy-FilesWithRobocopy @params

            Write-EnhancedLog -Message "Backup of Outlook Signatures to OneDrive completed successfully." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Backup-OutlookSignaturesToOneDrive function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Backup-OutlookSignaturesToOneDrive function" -Level "Notice"
    }
}

# # Example usage
# $params = @{
# SignaturePath = "$env:USERPROFILE\AppData\Roaming\Microsoft\Signatures"
# BackupFolderName = "OutlookSignatures"
# Exclude = ".git"
# RetryCount = 2
# WaitTime = 5
# RequiredSpaceGB = 10
# }
# Backup-OutlookSignaturesToOneDrive @params
#EndRegion '.\Public\Backup-OutlookSignaturestoOneDrive.ps1' 127
#Region '.\Public\Block-UserInput.ps1' -1

function Block-UserInput {
    <#
    .SYNOPSIS
    Blocks or unblocks user input.
 
    .DESCRIPTION
    The Block-UserInput function blocks or unblocks user input using the user32.dll library. This can be useful during critical operations to prevent user interference.
 
    .PARAMETER Block
    A boolean value indicating whether to block (true) or unblock (false) user input.
 
    .EXAMPLE
    $params = @{
        Block = $true
    }
    Block-UserInput @params
    Blocks user input.
 
    .EXAMPLE
    $params = @{
        Block = $false
    }
    Block-UserInput @params
    Unblocks user input.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [bool]$Block
    )

    Begin {
        Write-EnhancedLog -Message "Starting Block-UserInput function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            $code = @"
    [DllImport("user32.dll")]
    public static extern bool BlockInput(bool fBlockIt);
"@

            $userInput = Add-Type -MemberDefinition $code -Name Blocker -Namespace UserInput -PassThru

            Write-EnhancedLog -Message "Blocking user input: $Block" -Level "INFO"
            $null = $userInput::BlockInput($Block)
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Block-UserInput function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Block-UserInput function" -Level "Notice"
    }
}

# # Example usage
# $params = @{
# Block = $true
# }
# Block-UserInput @params
#EndRegion '.\Public\Block-UserInput.ps1' 66
#Region '.\Public\Check-OneDriveBackupStatus.ps1' -1

function Check-OneDriveBackupStatus {
    [CmdletBinding()]
    param ()

    Begin {
        Write-EnhancedLog -Message "Starting Check-OneDriveBackupStatus function" -Level "INFO"
    }

    Process {
        try {
            # Attempt to find the OneDrive directory
            $oneDriveDirectory = (Get-ChildItem "$env:USERPROFILE" -Filter "OneDrive - *" -Directory).FullName

            # Check if the OneDrive directory exists
            if (-not $oneDriveDirectory) {
                Write-EnhancedLog -Message "OneDrive directory does not exist. Remediation is not possible for now." -Level "WARNING"
                exit 0
            }

            # Define the backup path within the OneDrive directory
            $backupPath = Join-Path $oneDriveDirectory "DownloadsBackup"

            # Check if the DownloadsBackup folder exists and contains files
            if (Test-Path $backupPath) {
                $fileCount = (Get-ChildItem -Path $backupPath -Recurse -File).Count
                if ($fileCount -gt 0) {
                    Write-EnhancedLog -Message "DownloadsBackup folder detected with files at $backupPath. Remediation needed." -Level "WARNING"
                    exit 1
                } else {
                    Write-EnhancedLog -Message "DownloadsBackup folder exists at $backupPath but is empty. Remediation needed." -Level "WARNING"
                    exit 1
                }
            } else {
                Write-EnhancedLog -Message "DownloadsBackup folder does not exist at $backupPath. Remediation needed." -Level "WARNING"
                exit 1
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Check-OneDriveBackupStatus function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Check-OneDriveBackupStatus function" -Level "INFO"
    }
}

# Example usage
# Check-OneDriveBackupStatus
#EndRegion '.\Public\Check-OneDriveBackupStatus.ps1' 51
#Region '.\Public\Check-OneDriveSyncStatus.ps1' -1

function Check-OneDriveSyncStatus {
    [CmdletBinding()]
    param (
        [string]$OneDriveLibPath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Check-OneDriveSyncStatus function" -Level "Notice"
        Log-Params -Params @{ OneDriveLibPath = $OneDriveLibPath }

        # Check if running elevated
        # $isElevated = (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

        # if ($isElevated) {
        # Write-EnhancedLog -Message "Session is running elevated. Skipping Check-OneDriveSyncStatus function." -Level "INFO"
        # return
        # }

        # CheckAndElevate

        # Import OneDriveLib.dll to check current OneDrive Sync Status
        Import-Module $OneDriveLibPath
    }

    Process {
        if ($isElevated) {
            return
        }

        try {
            $Status = Get-ODStatus

            if (-not $Status) {
                Write-EnhancedLog -Message "OneDrive is not running or the user is not logged in to OneDrive." -Level "WARNING"
                return
            }

            # Create objects with known statuses listed.
            $Success = @( "Shared", "UpToDate", "Up To Date" )
            $InProgress = @( "SharedSync", "Shared Sync", "Syncing" )
            $Failed = @( "Error", "ReadOnly", "Read Only", "OnDemandOrUnknown", "On Demand or Unknown", "Paused")

            # Multiple OD4B accounts may be found. Consider adding logic to identify correct OD4B. Iterate through all accounts to check status and log the result.
            ForEach ($s in $Status) {
                $StatusString = $s.StatusString
                $DisplayName = $s.DisplayName
                $User = $s.UserName

                if ($s.StatusString -in $Success) {
                    Write-EnhancedLog -Message "OneDrive sync status is healthy: Display Name: $DisplayName, User: $User, Status: $StatusString" -Level "INFO"
                }
                elseif ($s.StatusString -in $InProgress) {
                    Write-EnhancedLog -Message "OneDrive sync status is currently syncing: Display Name: $DisplayName, User: $User, Status: $StatusString" -Level "INFO"
                }
                elseif ($s.StatusString -in $Failed) {
                    Write-EnhancedLog -Message "OneDrive sync status is in a known error state: Display Name: $DisplayName, User: $User, Status: $StatusString" -Level "ERROR"
                }
                elseif (-not $s.StatusString) {
                    Write-EnhancedLog -Message "Unable to get OneDrive Sync Status for Display Name: $DisplayName, User: $User" -Level "WARNING"
                }

                if (-not $Status.StatusString) {
                    Write-EnhancedLog -Message "Unable to get OneDrive Sync Status." -Level "ERROR"
                }
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while checking OneDrive sync status: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Check-OneDriveSyncStatus function" -Level "Notice"
    }
}

# Example usage
# $params = @{
# OneDriveLibPath = "C:\ProgramData\AADMigration\Files\OneDriveLib.dll"
# }
# Check-OneDriveSyncStatus @params
#EndRegion '.\Public\Check-OneDriveSyncStatus.ps1' 83
#Region '.\Public\Clear-OneDriveCache.ps1' -1

function Clear-OneDriveCache {
    <#
    .SYNOPSIS
    Clears the OneDrive cache.
   
    .DESCRIPTION
    The Clear-OneDriveCache function clears the OneDrive cache by restarting the OneDrive process.
   
    .EXAMPLE
    Clear-OneDriveCache
    Clears the OneDrive cache by restarting the OneDrive process.
    #>

  
    [CmdletBinding()]
    param ()
  
    Begin {
        Write-EnhancedLog -Message "Starting Clear-OneDriveCache function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }
  
    Process {
        try {
            Write-EnhancedLog -Message "Restarting OneDrive process to clear cache" -Level "INFO"
            $oneDrivePath = "C:\Program Files (x86)\Microsoft OneDrive\OneDrive.exe"
            if (Test-Path -Path $oneDrivePath) {
                Stop-Process -Name "OneDrive" -Force -ErrorAction SilentlyContinue
                Start-Process -FilePath $oneDrivePath -ErrorAction Stop
                Write-EnhancedLog -Message "Successfully restarted OneDrive process" -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "OneDrive executable not found at path: $oneDrivePath" -Level "WARNING"
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Clear-OneDriveCache function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }
  
    End {
        Write-EnhancedLog -Message "Exiting Clear-OneDriveCache function" -Level "Notice"
    }
  }
  
  # Example usage
  # Clear-OneDriveCache
#EndRegion '.\Public\Clear-OneDriveCache.ps1' 49
#Region '.\Public\Create-EventLogSource.ps1' -1

function Create-EventLogSource {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$LogName,
        [Parameter(Mandatory = $true)]
        [string]$Source
    )

    Begin {
        Write-EnhancedLog -Message "Starting Create-EventLogSource function" -Level "INFO"
        Log-Params -Params @{
            LogName = $LogName
            Source  = $Source
        }
    }

    Process {
        try {
            if (-not (Get-EventLog -LogName $LogName -Source $Source -ErrorAction SilentlyContinue)) {
                New-EventLog -LogName $LogName -Source $Source -ErrorAction Stop
            }
        } catch {
            Write-EnhancedLog -Message "An error occurred while creating the event log source: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Create-EventLogSource function" -Level "INFO"
    }
}


# $CreateEventLogSourceParams = @{
# LogName = "Application"
# Source = "AAD_Migration_Script"
# }

# Create-EventLogSource @CreateEventLogSourceParams
#EndRegion '.\Public\Create-EventLogSource.ps1' 41
#Region '.\Public\Create-OneDriveRemediationTask.ps1' -1

function Create-OneDriveRemediationTask {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$OneDriveExePath,

        [Parameter(Mandatory = $true)]
        [string]$ScheduledTaskName,

        [Parameter(Mandatory = $true)]
        [string]$ScheduledTaskDescription,

        [Parameter(Mandatory = $false)]
        [string]$ScheduledTaskArgumentList
    )

    Begin {
        Write-EnhancedLog -Message "Starting Create-OneDriveRemediationTask function" -Level "INFO"
        Log-Params -Params @{
            OneDriveExePath            = $OneDriveExePath
            ScheduledTaskName          = $ScheduledTaskName
            ScheduledTaskDescription   = $ScheduledTaskDescription
            ScheduledTaskArgumentList  = $ScheduledTaskArgumentList
        }
    }

    Process {
        try {
            # $userId = (Get-WmiObject -Class Win32_ComputerSystem).UserName
            $userId = $env:UserName
            if (-not $userId) {
                throw "Unable to retrieve the current user ID."
            }

            Write-EnhancedLog -Message "User ID retrieved: $userId" -Level "INFO"

            $actionParams = @{
                Execute  = $OneDriveExePath
            }
            if ($ScheduledTaskArgumentList) {
                $actionParams.Argument = $ScheduledTaskArgumentList
            }
            $action = New-ScheduledTaskAction @actionParams

            $trigger = New-ScheduledTaskTrigger -AtLogOn

            $principalParams = @{
                UserId = $userId
            }
            $principal = New-ScheduledTaskPrincipal @principalParams

            $taskParams = @{
                Action      = $action
                Trigger     = $trigger
                Principal   = $principal
                TaskName    = $ScheduledTaskName
                Description = $ScheduledTaskDescription
                Force       = $true
            }
            $task = Register-ScheduledTask @taskParams

            Start-ScheduledTask -TaskName $ScheduledTaskName

            # $DBG
            Start-Sleep -Seconds 5
            Unregister-ScheduledTask -TaskName $ScheduledTaskName -Confirm:$false
        } catch {
            Write-EnhancedLog -Message "An error occurred in Create-OneDriveRemediationTask function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Create-OneDriveRemediationTask function" -Level "INFO"
    }
}


# $CreateOneDriveRemediationTaskParams = @{
# OneDriveExePath = "C:\Program Files\Microsoft OneDrive\OneDrive.exe"
# ScheduledTaskName = "OneDriveRemediation"
# ScheduledTaskDescription = "Restart OneDrive to kick off KFM sync"
# ScheduledTaskArgumentList = ""
# }

# Create-OneDriveRemediationTask @CreateOneDriveRemediationTaskParams
#EndRegion '.\Public\Create-OneDriveRemediationTask.ps1' 87
#Region '.\Public\Create-OneDriveSyncStatusTask.ps1' -1

function Create-OneDriveSyncStatusTask {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$TaskPath,
        [Parameter(Mandatory = $true)]
        [string]$TaskName,
        [Parameter(Mandatory = $true)]
        [string]$ScriptDirectory,
        [Parameter(Mandatory = $true)]
        [string]$ScriptName,
        [Parameter(Mandatory = $true)]
        [string]$TaskArguments,
        [Parameter(Mandatory = $true)]
        [string]$TaskRepetitionDuration,
        [Parameter(Mandatory = $true)]
        [string]$TaskRepetitionInterval,
        [Parameter(Mandatory = $true)]
        [string]$TaskPrincipalGroupId,
        [Parameter(Mandatory = $true)]
        [string]$PowerShellPath,
        [Parameter(Mandatory = $true)]
        [string]$TaskDescription
    )

    Begin {
        Write-EnhancedLog -Message "Starting Create-OneDriveSyncStatusTask function" -Level "Notice"
        Log-Params -Params @{
            TaskPath               = $TaskPath
            TaskName               = $TaskName
            ScriptDirectory        = $ScriptDirectory
            ScriptName             = $ScriptName
            TaskArguments          = $TaskArguments
            TaskRepetitionDuration = $TaskRepetitionDuration
            TaskRepetitionInterval = $TaskRepetitionInterval
            TaskPrincipalGroupId   = $TaskPrincipalGroupId
            PowerShellPath         = $PowerShellPath
            TaskDescription        = $TaskDescription
        }
    }

    Process {
        try {
            $arguments = $TaskArguments.Replace("{ScriptPath}", "$ScriptDirectory\$ScriptName")

            $actionParams = @{
                Execute = $PowerShellPath
                Argument = $arguments
            }
            $action = New-ScheduledTaskAction @actionParams

            $triggerParams = @{
                AtLogOn = $true
            }
            $trigger = New-ScheduledTaskTrigger @triggerParams

            $principalParams = @{
                GroupId = $TaskPrincipalGroupId
            }
            $principal = New-ScheduledTaskPrincipal @principalParams

            $registerTaskParams = @{
                Principal = $principal
                Action = $action
                Trigger = $trigger
                TaskName = $TaskName
                Description = $TaskDescription
                TaskPath = $TaskPath
            }
            $Task = Register-ScheduledTask @registerTaskParams

            $Task.Triggers.Repetition.Duration = $TaskRepetitionDuration
            $Task.Triggers.Repetition.Interval = $TaskRepetitionInterval
            $Task | Set-ScheduledTask
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while creating the OneDrive sync status task: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Create-OneDriveSyncStatusTask function" -Level "Notice"
    }
}

# # Example usage with splatting
# $CreateOneDriveSyncStatusTaskParams = @{
# TaskPath = "AAD Migration"
# TaskName = "AADM Get OneDrive Sync Status"
# ScriptDirectory = "C:\ProgramData\AADMigration\Scripts"
# ScriptName = "Check-OneDriveSyncStatus.ps1"
# TaskArguments = "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -file `"{ScriptPath}`""
# TaskRepetitionDuration = "P1D"
# TaskRepetitionInterval = "PT30M"
# TaskPrincipalGroupId = "BUILTIN\Users"
# PowerShellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
# TaskDescription = "Get current OneDrive Sync Status and write to event log"
# }

# Create-OneDriveSyncStatusTask @CreateOneDriveSyncStatusTaskParams
#EndRegion '.\Public\Create-OneDriveSyncStatusTask.ps1' 102
#Region '.\Public\Create-PPKG.ps1' -1

function Create-PPKG {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ICDPath,

        [Parameter(Mandatory = $true)]
        [string]$CustomizationXMLPath,

        [Parameter(Mandatory = $true)]
        [string]$PackagePath,

        [Parameter(Mandatory = $false)]
        [string]$ProductName,

        [Parameter(Mandatory = $false)]
        [string]$StoreFile,

        [Parameter(Mandatory = $false)]
        [string]$MSPackageRoot,

        [Parameter(Mandatory = $false)]
        [string]$OEMInputXML,

        [Parameter(Mandatory = $false)]
        [hashtable]$Variables,

        [Parameter(Mandatory = $false)]
        [bool]$Encrypted = $false,

        [Parameter(Mandatory = $false)]
        [bool]$Overwrite = $true
    )

    Begin {
        Write-EnhancedLog -Message "Starting Create-PPKG function" -Level "INFO"
        Log-Params -Params @{
            ICDPath = $ICDPath
            CustomizationXMLPath = $CustomizationXMLPath
            PackagePath = $PackagePath
            ProductName = $ProductName
            StoreFile = $StoreFile
            MSPackageRoot = $MSPackageRoot
            OEMInputXML = $OEMInputXML
            Variables = $Variables
            Encrypted = $Encrypted
            Overwrite = $Overwrite
        }

        # Ensure ICD.exe exists
        if (-not (Test-Path -Path $ICDPath)) {
            throw "ICD.exe not found at: $ICDPath"
        }

        # Ensure Customization XML file exists
        if (-not (Test-Path -Path $CustomizationXMLPath)) {
            throw "Customization XML file not found at: $CustomizationXMLPath"
        }
    }

    Process {
        try {
            # Build the command line arguments using a list
            $ICD_args = [System.Collections.Generic.List[string]]::new()
            $ICD_args.Add("/Build-ProvisioningPackage")
            $ICD_args.Add("/CustomizationXML:`"$CustomizationXMLPath`"")
            $ICD_args.Add("/PackagePath:`"$PackagePath`"")

            if ($Encrypted) {
                $ICD_args.Add("+Encrypted")
            } else {
                $ICD_args.Add("-Encrypted")
            }

            if ($Overwrite) {
                $ICD_args.Add("+Overwrite")
            } else {
                $ICD_args.Add("-Overwrite")
            }

            if ($ProductName) {
                $ICD_args.Add("/ProductName:`"$ProductName`"")
            }

            if ($StoreFile) {
                $ICD_args.Add("/StoreFile:`"$StoreFile`"")
            }

            if ($MSPackageRoot) {
                $ICD_args.Add("/MSPackageRoot:`"$MSPackageRoot`"")
            }

            if ($OEMInputXML) {
                $ICD_args.Add("/OEMInputXML:`"$OEMInputXML`"")
            }

            if ($Variables) {
                foreach ($key in $Variables.Keys) {
                    $ICD_args.Add("/Variables:`"$key=$($Variables[$key])`"")
                }
            }

            $ICD_args_string = $ICD_args -join " "
            Write-EnhancedLog -Message "Running ICD.exe with arguments: $ICD_args_string" -Level "INFO"
            Start-Process -FilePath $ICDPath -ArgumentList $ICD_args_string -Wait -NoNewWindow

        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the Create-PPKG function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Create-PPKG function" -Level "INFO"
    }
}

# Example usage
# $ppkgParams = @{
# ICDPath = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Imaging and Configuration Designer\x86\ICD.exe"
# CustomizationXMLPath = "C:\code\CB\Entra\DeviceMigration\Files\customizations.xml"
# PackagePath = "C:\code\CB\Entra\DeviceMigration\Files\ProvisioningPackage.ppkg"
# Encrypted = $false
# Overwrite = $true
# }

# Create-PPKG @ppkgParams
#EndRegion '.\Public\Create-PPKG.ps1' 128
#Region '.\Public\Create-ProvPackageAADEnrollment-Archive.ps1' -1

# function Create-ProvPackageAADEnrollment {
# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [string]$Source,

# [Parameter(Mandatory = $true)]
# [string]$Destination
# )

# Begin {
# Write-EnhancedLog -Message "Starting Create-ProvPackage-AADEnrollment function" -Level "INFO"
# Log-Params -Params @{
# Source = $Source
# Destination = $Destination
# }
# }

# Process {
# try {
# # Create a provisioning package for AAD bulk enrollment (placeholder as the actual creation depends on your environment)
# # Assuming you have a script or method to create it
# # Copy the provisioning package to the Files folder
# Copy-Item -Path $Source -Destination $Destination -Force
# } catch {
# Write-EnhancedLog -Message "An error occurred while processing the Create-ProvPackage-AADEnrollment function: $($_.Exception.Message)" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Create-ProvPackage-AADEnrollment function" -Level "INFO"
# }
# }

# # Example usage
# # Create-ProvPackageAADEnrollment -Source 'C:\Path\To\Your\ProvisioningPackage.ppkg' -Destination 'C:\YourPath\Files\ProvisioningPackage.ppkg'
#EndRegion '.\Public\Create-ProvPackageAADEnrollment-Archive.ps1' 38
#Region '.\Public\Disable-LocalUserAccounts.ps1' -1

function Disable-LocalUserAccounts {
    <#
    .SYNOPSIS
    Disables all enabled local user accounts except for default accounts.
   
    .DESCRIPTION
    The Disable-LocalUserAccounts function disables all enabled local user accounts except for default accounts.
   
    .EXAMPLE
    Disable-LocalUserAccounts
    Disables all enabled local user accounts except for default accounts.
    #>

  
    [CmdletBinding()]
    param ()
  
    Begin {
        Write-EnhancedLog -Message "Starting Disable-LocalUserAccounts function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }
  
    Process {
        try {
            $users = Get-LocalUser | Where-Object { $_.Enabled -eq $true -and $_.Name -notlike 'default*' }
            foreach ($user in $users) {
                Write-EnhancedLog -Message "Disabling local user account: $($user.Name)" -Level "INFO"
                Disable-LocalUser -Name $user.Name -ErrorAction Stop
                Write-EnhancedLog -Message "Successfully disabled local user account: $($user.Name)" -Level "INFO"
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Disable-LocalUserAccounts function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }
  
    End {
        Write-EnhancedLog -Message "Exiting Disable-LocalUserAccounts function" -Level "Notice"
    }
  }


  # Example usage
# Disable-LocalUserAccounts
#EndRegion '.\Public\Disable-LocalUserAccounts.ps1' 46
#Region '.\Public\Disable-OOBEPrivacy.ps1' -1

function Disable-OOBEPrivacy {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$OOBERegistryPath,

        [Parameter(Mandatory = $true)]
        [string]$OOBEName,

        [Parameter(Mandatory = $true)]
        [string]$OOBEValue,

        [Parameter(Mandatory = $true)]
        [string]$AnimationRegistryPath,

        [Parameter(Mandatory = $true)]
        [string]$AnimationName,

        [Parameter(Mandatory = $true)]
        [string]$AnimationValue,

        [Parameter(Mandatory = $true)]
        [string]$LockRegistryPath,

        [Parameter(Mandatory = $true)]
        [string]$LockName,

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

    Begin {
        Write-EnhancedLog -Message "Starting Disable-OOBEPrivacy function" -Level "INFO"
        Log-Params -Params @{
            OOBERegistryPath      = $OOBERegistryPath
            OOBEName              = $OOBEName
            OOBEValue             = $OOBEValue
            AnimationRegistryPath = $AnimationRegistryPath
            AnimationName         = $AnimationName
            AnimationValue        = $AnimationValue
            LockRegistryPath      = $LockRegistryPath
            LockName              = $LockName
            LockValue             = $LockValue
        }
    }

    Process {
        try {
            Write-EnhancedLog -Message "Disabling privacy experience" -Level "INFO"
            if (-not (Test-Path -Path $OOBERegistryPath)) {
                New-Item -Path $OOBERegistryPath -Force | Out-Null
            }
            New-ItemProperty -Path $OOBERegistryPath -Name $OOBEName -Value $OOBEValue -PropertyType DWORD -Force -Verbose

            Write-EnhancedLog -Message "Disabling first logon animation" -Level "INFO"
            if (-not (Test-Path -Path $AnimationRegistryPath)) {
                New-Item -Path $AnimationRegistryPath -Force | Out-Null
            }
            New-ItemProperty -Path $AnimationRegistryPath -Name $AnimationName -Value $AnimationValue -PropertyType DWORD -Force -Verbose

            Write-EnhancedLog -Message "Removing lock screen" -Level "INFO"
            if (-not (Test-Path -Path $LockRegistryPath)) {
                New-Item -Path $LockRegistryPath -Force | Out-Null
            }
            New-ItemProperty -Path $LockRegistryPath -Name $LockName -Value $LockValue -PropertyType DWORD -Force -Verbose
        } catch {
            Write-EnhancedLog -Message "An error occurred while disabling OOBE privacy: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Disable-OOBEPrivacy function" -Level "INFO"
    }
}

# # Example usage with splatting
# $DisableOOBEPrivacyParams = @{
# OOBERegistryPath = 'HKLM:\Software\Policies\Microsoft\Windows\OOBE'
# OOBEName = 'DisablePrivacyExperience'
# OOBEValue = '1'
# AnimationRegistryPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
# AnimationName = 'EnableFirstLogonAnimation'
# AnimationValue = '0'
# LockRegistryPath = 'HKLM:\Software\Policies\Microsoft\Windows\Personalization'
# LockName = 'NoLockScreen'
# LockValue = '1'
# }

# Disable-OOBEPrivacy @DisableOOBEPrivacyParams
#EndRegion '.\Public\Disable-OOBEPrivacy.ps1' 91
#Region '.\Public\Download-ADKOffline.ps1' -1

function Download-ADKOffline {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ADKUrl = "https://go.microsoft.com/fwlink/?linkid=2271337",

        [Parameter(Mandatory = $true)]
        [string]$DownloadPath = "$env:TEMP\adksetup.exe",

        [Parameter(Mandatory = $true)]
        [string]$OfflinePath = "$env:TEMP\ADKOffline"
    )

    Begin {
        Write-EnhancedLog -Message "Starting Download-ADKOffline function" -Level "INFO"
        Log-Params -Params @{
            ADKUrl = $ADKUrl
            DownloadPath = $DownloadPath
            OfflinePath = $OfflinePath
        }
    }

    Process {
        try {
            # Download the ADK setup file
            Write-EnhancedLog -Message "Downloading ADK from: $ADKUrl to: $DownloadPath" -Level "INFO"
            Invoke-WebRequest -Uri $ADKUrl -OutFile $DownloadPath

            # Create offline path if it does not exist
            if (-not (Test-Path -Path $OfflinePath)) {
                New-Item -ItemType Directory -Path $OfflinePath -Force
            }

            # Download the ADK components for offline installation
            Write-EnhancedLog -Message "Downloading ADK components for offline installation to: $OfflinePath" -Level "INFO"
            Start-Process -FilePath $DownloadPath -ArgumentList "/quiet", "/layout $OfflinePath" -Wait -NoNewWindow
        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the Download-ADKOffline function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Download-ADKOffline function" -Level "INFO"
    }
}

# Example usage
# $adkParams = @{
# ADKUrl = 'https://go.microsoft.com/fwlink/?linkid=2271337'
# DownloadPath = "$env:TEMP\adksetup.exe"
# OfflinePath = "$env:TEMP\ADKOffline"
# }

# Download-ADKOffline @adkParams
#EndRegion '.\Public\Download-ADKOffline.ps1' 56
#Region '.\Public\Download-InstallMDT.ps1' -1

# function Download-InstallMDT {
# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [string]$Url,

# [Parameter(Mandatory = $true)]
# [string]$Destination,

# [Parameter(Mandatory = $true)]
# [string]$FilesFolder
# )

# Begin {
# Write-EnhancedLog -Message "Starting Download-Install-MDT function" -Level "INFO"
# Log-Params -Params @{
# Url = $Url
# Destination = $Destination
# FilesFolder = $FilesFolder
# }
# }

# Process {
# try {
# # Download and install Microsoft Deployment Toolkit
# Invoke-WebRequest -Uri $Url -OutFile $Destination
# Start-Process -FilePath $Destination -ArgumentList "/quiet" -Wait

# # Copy ServiceUI.exe to Files folder
# Copy-Item -Path "C:\Program Files\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x64\ServiceUI.exe" -Destination $FilesFolder
# } catch {
# Write-EnhancedLog -Message "An error occurred while processing the Download-InstallMDT function: $($_.Exception.Message)" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Download-Install-MDT function" -Level "INFO"
# }
# }

# # Example usage
# # Download-InstallMDT -Url 'https://download.microsoft.com/download/9/e/1/9e1e94ec-5463-46b7-9f3c-b225034c3a70/MDT_KB4564442.exe' -Destination 'C:\YourPath\Files\MDT.exe' -FilesFolder 'C:\YourPath\Files'
#EndRegion '.\Public\Download-InstallMDT.ps1' 44
#Region '.\Public\Download-MigrationTool.ps1' -1

function Download-MigrationTool {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Url,

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

    Begin {
        Write-EnhancedLog -Message "Starting Download-MigrationTool function" -Level "INFO"
        Log-Params -Params @{
            Url = $Url
            Destination = $Destination
        }
    }

    Process {
        try {
            # Download Migration Tool
            Invoke-WebRequest -Uri $Url -OutFile $Destination
        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the Download-MigrationTool function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Download-MigrationTool function" -Level "INFO"
    }
}

# Example usage
# Download-MigrationTool -Url "https://example.com/tool.zip" -Destination "C:\path\to\destination"
#EndRegion '.\Public\Download-MigrationTool.ps1' 36
#Region '.\Public\Download-OneDriveLib.ps1' -1

function Download-OneDriveLib {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Destination
    )

    Begin {
        Write-EnhancedLog -Message "Starting Download-OneDriveLib function" -Level "INFO"
        Log-Params -Params @{
            Destination = $Destination
        }
    }

    Process {
        try {
            # Get the latest release info from GitHub
            $latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/rodneyviana/ODSyncService/releases/latest"
            $url = $latestRelease.assets | Where-Object { $_.name -eq "OneDriveLib.dll" } | Select-Object -ExpandProperty browser_download_url

            if (-not $url) {
                throw "No matching file found for OneDriveLib.dll"
            }

            Write-EnhancedLog -Message "Downloading OneDriveLib.dll from: $url" -Level "INFO"
            
            # Download the file
            Invoke-WebRequest -Uri $url -OutFile $Destination
        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the Download-OneDriveLib function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Download-OneDriveLib function" -Level "INFO"
    }
}

# Example usage
# Download-OneDriveLib -Destination 'C:\YourPath\Files\OneDriveLib.dll'
#EndRegion '.\Public\Download-OneDriveLib.ps1' 42
#Region '.\Public\Download-PSAppDeployToolkit-Archive.ps1' -1

# function Download-PSAppDeployToolkit {
# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [string]$Url,

# [Parameter(Mandatory = $true)]
# [string]$Destination,

# [Parameter(Mandatory = $true)]
# [string]$ToolkitFolder
# )

# Begin {
# Write-EnhancedLog -Message "Starting Download-PSAppDeployToolkit function" -Level "INFO"
# Log-Params -Params @{
# Url = $Url
# Destination = $Destination
# ToolkitFolder = $ToolkitFolder
# }
# }

# Process {
# try {
# # Download and extract PSAppDeployToolkit
# Invoke-WebRequest -Uri $Url -OutFile $Destination
# Expand-Archive -Path $Destination -DestinationPath $ToolkitFolder -Force
# } catch {
# Write-EnhancedLog -Message "An error occurred while processing the Download-PSAppDeployToolkit function: $($_.Exception.Message)" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Download-PSAppDeployToolkit function" -Level "INFO"
# }
# }

# # Example usage
# # Download-PSAppDeployToolkit -Url 'https://github.com/PSAppDeployToolkit/PSAppDeployToolkit/releases/download/v3.8.4/PSAppDeployToolkit_v3.8.4.zip' -Destination 'C:\YourPath\Files\PSAppDeployToolkit.zip' -ToolkitFolder 'C:\YourPath\Toolkit'
#EndRegion '.\Public\Download-PSAppDeployToolkit-Archive.ps1' 41
#Region '.\Public\Escrow-BitLockerKey.ps1' -1

function Test-Bitlocker {
    <#
    .SYNOPSIS
    Tests if BitLocker is enabled on the specified drive.
 
    .DESCRIPTION
    The Test-Bitlocker function tests if BitLocker is enabled on the specified drive.
 
    .PARAMETER BitlockerDrive
    The drive letter of the BitLocker protected drive.
 
    .EXAMPLE
    Test-Bitlocker -BitlockerDrive "C:"
    Tests if BitLocker is enabled on drive C:.
    #>


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

    Begin {
        Write-EnhancedLog -Message "Starting Test-Bitlocker function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            $bitlockerVolume = Get-BitLockerVolume -MountPoint $BitlockerDrive -ErrorAction Stop
            Write-EnhancedLog -Message "BitLocker is enabled on drive: $BitlockerDrive" -Level "INFO"
            return $bitlockerVolume
        }
        catch {
            Write-EnhancedLog -Message "BitLocker is not enabled on drive: $BitlockerDrive. Terminating script!" -Level "ERROR"
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Test-Bitlocker function" -Level "Notice"
    }
}

function Get-KeyProtectorId {
    <#
    .SYNOPSIS
    Retrieves the key protector ID for the specified drive.
 
    .DESCRIPTION
    The Get-KeyProtectorId function retrieves the key protector ID for the specified BitLocker protected drive.
 
    .PARAMETER BitlockerDrive
    The drive letter of the BitLocker protected drive.
 
    .EXAMPLE
    Get-KeyProtectorId -BitlockerDrive "C:"
    Retrieves the key protector ID for drive C:.
    #>


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

    Begin {
        Write-EnhancedLog -Message "Starting Get-KeyProtectorId function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            $bitlockerVolume = Get-BitLockerVolume -MountPoint $BitlockerDrive
            $keyProtector = $bitlockerVolume.KeyProtector | Where-Object { $_.KeyProtectorType -eq 'RecoveryPassword' }
            Write-EnhancedLog -Message "Retrieved key protector ID for drive: $BitlockerDrive" -Level "INFO"
            return $keyProtector.KeyProtectorId
        }
        catch {
            Write-EnhancedLog -Message "Failed to retrieve key protector ID for drive: $BitlockerDrive" -Level "ERROR"
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Get-KeyProtectorId function" -Level "Notice"
    }
}

function Invoke-BitlockerEscrow {
    <#
    .SYNOPSIS
    Escrows the BitLocker recovery key to Azure AD.
 
    .DESCRIPTION
    The Invoke-BitlockerEscrow function escrows the BitLocker recovery key for the specified drive to Azure AD.
 
    .PARAMETER BitlockerDrive
    The drive letter of the BitLocker protected drive.
 
    .PARAMETER BitlockerKey
    The key protector ID to be escrowed.
 
    .EXAMPLE
    Invoke-BitlockerEscrow -BitlockerDrive "C:" -BitlockerKey "12345678-1234-1234-1234-123456789012"
    Escrows the BitLocker recovery key for drive C: to Azure AD.
    #>


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

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

    Begin {
        Write-EnhancedLog -Message "Starting Invoke-BitlockerEscrow function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            Write-EnhancedLog -Message "Escrowing the BitLocker recovery key to Azure AD for drive: $BitlockerDrive" -Level "INFO"
            BackupToAAD-BitLockerKeyProtector -MountPoint $BitlockerDrive -KeyProtectorId $BitlockerKey -ErrorAction SilentlyContinue
            Write-EnhancedLog -Message "Attempted to escrow key in Azure AD - Please verify manually!" -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while escrowing the BitLocker key to Azure AD" -Level "ERROR"
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Invoke-BitlockerEscrow function" -Level "Notice"
    }
}

function Escrow-BitLockerKey {
    <#
    .SYNOPSIS
    Escrows the BitLocker recovery key to Azure AD.
 
    .DESCRIPTION
    The Escrow-BitLockerKey function tests if BitLocker is enabled on the specified drive, retrieves the key protector ID, and escrows the BitLocker recovery key to Azure AD.
 
    .PARAMETER DriveLetter
    The drive letter of the BitLocker protected drive.
 
    .EXAMPLE
    $params = @{
        DriveLetter = "C:"
    }
    Escrow-BitLockerKey @params
    Escrows the BitLocker recovery key for drive C: to Azure AD.
    #>


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

    Begin {
        Write-EnhancedLog -Message "Starting Escrow-BitLockerKey function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            $bitlockerVolume = Test-Bitlocker -BitlockerDrive $DriveLetter
            $keyProtectorId = Get-KeyProtectorId -BitlockerDrive $DriveLetter
            Invoke-BitlockerEscrow -BitlockerDrive $DriveLetter -BitlockerKey $keyProtectorId
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Escrow-BitLockerKey function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Escrow-BitLockerKey function" -Level "Notice"
    }
}

# # Example usage
# $params = @{
# DriveLetter = "C:"
# }
# Escrow-BitLockerKey @params
#EndRegion '.\Public\Escrow-BitLockerKey.ps1' 193
#Region '.\Public\Execute-MigrationToolkit.ps1' -1

function Execute-MigrationToolkit {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ServiceUI,

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

    Begin {
        Write-EnhancedLog -Message "Starting Execute-MigrationToolkit function" -Level "INFO"
        Log-Params -Params @{
            ServiceUI = $ServiceUI
            ExePath   = $ExePath
        }
    }

    Process {
        try {
            $targetProcesses = @(Get-WmiObject -Query "Select * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue)
            if ($targetProcesses.Count -eq 0) {
                Write-EnhancedLog -Message "No user logged in, running without ServiceUI" -Level "INFO"
                Start-Process -FilePath $ExePath -ArgumentList '-DeployMode "NonInteractive"' -Wait -NoNewWindow
            } else {
                foreach ($targetProcess in $targetProcesses) {
                    $Username = $targetProcess.GetOwner().User
                    Write-EnhancedLog -Message "$Username logged in, running with ServiceUI" -Level "INFO"
                }
                Start-Process -FilePath $ServiceUI -ArgumentList "-Process:explorer.exe $ExePath" -NoNewWindow
            }
        } catch {
            $ErrorMessage = $_.Exception.Message
            Write-EnhancedLog -Message "An error occurred: $ErrorMessage" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Install Exit Code = $LASTEXITCODE" -Level "INFO"
        Write-EnhancedLog -Message "Exiting Execute-MigrationToolkit function" -Level "INFO"
        Exit $LASTEXITCODE
    }
}

# # Define paths
# $ToolkitPaths = @{
# ServiceUI = "C:\ProgramData\AADMigration\Files\ServiceUI.exe"
# ExePath = "C:\ProgramData\AADMigration\Toolkit\Deploy-Application.exe"
# }

# # Example usage with splatting
# Execute-MigrationToolkit @ToolkitPaths
#EndRegion '.\Public\Execute-MigrationToolkit.ps1' 54
#Region '.\Public\Install-ADKFromMSI.ps1' -1

function Install-ADKFromMSI {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$OfflinePath,

        [Parameter(Mandatory = $true)]
        [string]$ICDPath = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Imaging and Configuration Designer\x86\ICD.exe"
    )

    Begin {
        Write-EnhancedLog -Message "Starting Install-ADKFromMSI function" -Level "INFO"
        Log-Params -Params @{
            OfflinePath = $OfflinePath
            ICDPath = $ICDPath
        }

        # Ensure offline path exists
        if (-not (Test-Path -Path $OfflinePath)) {
            throw "Offline path not found: $OfflinePath"
        }
    }

    Process {
        try {
            # Get all MSI files in the offline path
            $MSIFiles = Get-ChildItem -Path $OfflinePath -Filter *.msi

            if (-not $MSIFiles) {
                throw "No MSI files found in: $OfflinePath"
            }

            # Install each MSI file
            foreach ($MSI in $MSIFiles) {
                Write-EnhancedLog -Message "Installing MSI: $($MSI.FullName)" -Level "INFO"
                Start-Process -FilePath "msiexec.exe" -ArgumentList "/i `"$($MSI.FullName)`" /quiet /norestart" -Wait -NoNewWindow
            }

            # Check if ICD.exe exists
            if (Test-Path -Path $ICDPath) {
                Write-EnhancedLog -Message "ICD.exe found at: $ICDPath" -Level "INFO"
            } else {
                throw "ICD.exe not found at: $ICDPath"
            }

        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the Install-ADKFromMSI function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Install-ADKFromMSI function" -Level "INFO"
    }
}

# # Example usage
# $installParams = @{
# OfflinePath = "$env:TEMP\ADKOffline\Installers"
# ICDPath = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Imaging and Configuration Designer\x86\ICD.exe"
# }

# Install-ADKFromMSI @installParams
#EndRegion '.\Public\Install-ADKFromMSI.ps1' 64
#Region '.\Public\Install-OneDrive.ps1' -1

function Download-OneDriveSetup {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ODSetupUri,

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

    Begin {
        Write-EnhancedLog -Message "Starting Download-OneDriveSetup function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            Invoke-WebRequest -Uri $ODSetupUri -OutFile $ODSetupPath
            Write-EnhancedLog -Message "Downloaded OneDrive setup to $ODSetupPath" -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while downloading OneDrive setup: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Download-OneDriveSetup function" -Level "Notice"
    }
}

function Get-OneDriveSetupVersion {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ODSetupPath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Get-OneDriveSetupVersion function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            $ODSetupVersion = (Get-ChildItem -Path $ODSetupPath).VersionInfo.FileVersion
            Write-EnhancedLog -Message "OneDrive setup version: $ODSetupVersion" -Level "INFO"
            return $ODSetupVersion
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while getting OneDrive setup version: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Get-OneDriveSetupVersion function" -Level "Notice"
    }
}


function Get-InstalledOneDriveVersion {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ODRegKey
    )

    Begin {
        Write-EnhancedLog -Message "Starting Get-InstalledOneDriveVersion function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            $InstalledVer = if (Test-Path -Path $ODRegKey) {
                Get-ItemPropertyValue -Path $ODRegKey -Name Version
            } else {
                [System.Version]::new("0.0.0.0")
            }
            Write-EnhancedLog -Message "Installed OneDrive version: $InstalledVer" -Level "INFO"
            return $InstalledVer
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while getting installed OneDrive version: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Get-InstalledOneDriveVersion function" -Level "Notice"
    }
}


function Install-OneDriveSetup {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ODSetupPath,

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

    Begin {
        Write-EnhancedLog -Message "Starting Install-OneDriveSetup function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            Write-EnhancedLog -Message "Installing OneDrive setup" -Level "INFO"
            Start-Process -FilePath $ODSetupPath -ArgumentList $SetupArgumentList -Wait -NoNewWindow
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while installing OneDrive setup: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Install-OneDriveSetup function" -Level "Notice"
    }
}


function Perform-KFMSync {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$OneDriveExePath,

        [Parameter(Mandatory = $true)]
        [string]$ScheduledTaskName,

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

    Begin {
        Write-EnhancedLog -Message "Starting Perform-KFMSync function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            Write-EnhancedLog -Message "Performing KFM sync" -Level "INFO"
            $ODProcess = Get-Process -Name OneDrive -ErrorAction SilentlyContinue

            if ($ODProcess) {
                $ODProcess | Stop-Process -Confirm:$false -Force
                Start-Sleep -Seconds 5

                Unregister-ScheduledTaskWithLogging -TaskName $ScheduledTaskName

                $CreateOneDriveRemediationTaskParams = @{
                    OneDriveExePath           = $OneDriveExePath
                    ScheduledTaskName         = $ScheduledTaskName
                    ScheduledTaskDescription  = $ScheduledTaskDescription
                }
                
                Create-OneDriveRemediationTask @CreateOneDriveRemediationTaskParams
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while performing KFM sync: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Perform-KFMSync function" -Level "Notice"
    }
}


function Install-OneDrive {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$MigrationPath,

        [Parameter(Mandatory = $false)]
        [bool]$OneDriveKFM = $false,

        [Parameter(Mandatory = $true)]
        [string]$ODSetupUri,

        [Parameter(Mandatory = $true)]
        [string]$ODSetupFile,

        [Parameter(Mandatory = $true)]
        [string]$ODRegKey,

        [Parameter(Mandatory = $true)]
        [string]$OneDriveExePath,

        [Parameter(Mandatory = $true)]
        [string]$ScheduledTaskName,

        [Parameter(Mandatory = $true)]
        [string]$ScheduledTaskDescription,

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

    Begin {
        Write-EnhancedLog -Message "Starting Install-OneDrive function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        $ODSetupPath = Join-Path -Path $MigrationPath -ChildPath $ODSetupFile
        $ODSetupVersion = $null
    }

    Process {
        try {
            if (-not (Test-Path -Path $ODSetupPath)) {
                Download-OneDriveSetup -ODSetupUri $ODSetupUri -ODSetupPath $ODSetupPath
            }

            $ODSetupVersion = Get-OneDriveSetupVersion -ODSetupPath $ODSetupPath
            $InstalledVer = Get-InstalledOneDriveVersion -ODRegKey $ODRegKey

            if (-not $InstalledVer -or ([System.Version]$InstalledVer -lt [System.Version]$ODSetupVersion)) {
                Install-OneDriveSetup -ODSetupPath $ODSetupPath -SetupArgumentList $SetupArgumentList
            } elseif ($OneDriveKFM) {
                Perform-KFMSync -OneDriveExePath $OneDriveExePath -ScheduledTaskName $ScheduledTaskName -ScheduledTaskDescription $ScheduledTaskDescription
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Install-OneDrive function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Install-OneDrive function" -Level "Notice"
    }
}


# $InstallOneDriveParams = @{
# MigrationPath = "C:\ProgramData\AADMigration"
# OneDriveKFM = $true
# ODSetupUri = "https://go.microsoft.com/fwlink/?linkid=844652"
# ODSetupFile = "Files\OneDriveSetup.exe"
# ODRegKey = "HKLM:\SOFTWARE\Microsoft\OneDrive"
# OneDriveExePath = "C:\Program Files\Microsoft OneDrive\OneDrive.exe"
# ScheduledTaskName = "OneDriveRemediation"
# ScheduledTaskDescription = "Restart OneDrive to kick off KFM sync"
# ScheduledTaskArgumentList = ""
# SetupArgumentList = "/allusers"
# }

# Install-OneDrive @InstallOneDriveParams
#EndRegion '.\Public\Install-OneDrive.ps1' 264
#Region '.\Public\Install-PPKG.ps1' -1

function Install-PPKG {
    <#
    .SYNOPSIS
    Installs a provisioning package (PPKG).
 
    .DESCRIPTION
    The Install-PPKG function installs a provisioning package (PPKG) from a specified path. It logs the installation process and handles errors gracefully.
 
    .PARAMETER PPKGName
    The name of the provisioning package to be installed.
 
    .PARAMETER MigrationPath
    The path to the migration files directory containing the provisioning package.
 
    .EXAMPLE
    $params = @{
        PPKGName = "MyProvisioningPackage.ppkg"
        MigrationPath = "C:\ProgramData\AADMigration"
    }
    Install-PPKG @params
    Installs the specified provisioning package.
    #>


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

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

    Begin {
        Write-EnhancedLog -Message "Starting Install-PPKG function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            $ppkgPath = Join-Path -Path $MigrationPath -ChildPath "Files\$PPKGName"
            if (-not (Test-Path -Path $ppkgPath)) {
                Throw "Provisioning package file not found: $ppkgPath"
            }

            Write-EnhancedLog -Message "Installing provisioning package: $ppkgPath" -Level "INFO"

            $params = @{
                PackagePath  = $ppkgPath
                ForceInstall = $true
                QuietInstall = $true
            }

            Install-ProvisioningPackage @params
            Write-EnhancedLog -Message "Provisioning package installed successfully." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Install-PPKG function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Install-PPKG function" -Level "Notice"
    }
}

# # Example usage
# $params = @{
# PPKGName = "MyProvisioningPackage.ppkg"
# MigrationPath = "C:\ProgramData\AADMigration"
# }
# Install-PPKG @params
#EndRegion '.\Public\Install-PPKG.ps1' 74
#Region '.\Public\Main-MigrateToAADJOnly.ps1' -1

function Main-MigrateToAADJOnly {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$PPKGName,
        
        [Parameter(Mandatory = $false)]
        [string]$DomainLeaveUser,
        
        [Parameter(Mandatory = $false)]
        [string]$DomainLeavePassword,
        
        [Parameter(Mandatory = $true)]
        [string]$TempUser,
        
        [Parameter(Mandatory = $true)]
        [string]$TempUserPassword,
        
        [Parameter(Mandatory = $true)]
        [string]$ScriptPath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Main-MigrateToAADJOnly function" -Level "Notice"
        Log-Params -Params @{
            PPKGName            = $PPKGName;
            DomainLeaveUser     = $DomainLeaveUser;
            DomainLeavePassword = $DomainLeavePassword;
            TempUser            = $TempUser;
            TempUserPassword    = $TempUserPassword;
            ScriptPath          = $ScriptPath;
        }

        # Initialize steps
        $global:steps = @()
        Add-Step -description "Testing provisioning package" -action { 

            $TestProvisioningPackParams = @{
                PPKGName = $PPKGName
            }

            Test-ProvisioningPack @TestProvisioningPackParams

        }
        # Add-Step -description "Adding local user" -action { Add-LocalUser -TempUser $TempUser -TempUserPassword $TempUserPassword }
        Add-Step -description "Adding local user" -action { 
            $AddLocalUserParams = @{
                TempUser         = $TempUser
                TempUserPassword = $TempUserPassword
                Description      = "account for autologin"
                Group            = "Administrators"
            }
            
            # Example usage with splatting
            Add-LocalUser @AddLocalUserParams
        }
        Add-Step -description "Setting autologin" -action { 


            # Example usage with splatting
            $SetAutologinParams = @{
                TempUser            = $TempUser
                TempUserPassword    = $TempUserPassword
                RegPath             = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
                AutoAdminLogonName  = 'AutoAdminLogon'
                AutoAdminLogonValue = '1'
                DefaultUsernameName = 'DefaultUsername'
                DefaultPasswordName = 'DefaultPassword'
            }

            Set-Autologin @SetAutologinParams

        }
        Add-Step -description "Disabling OOBE privacy" -action { 


            # Example usage with splatting
            $DisableOOBEPrivacyParams = @{
                OOBERegistryPath      = 'HKLM:\Software\Policies\Microsoft\Windows\OOBE'
                OOBEName              = 'DisablePrivacyExperience'
                OOBEValue             = '1'
                AnimationRegistryPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
                AnimationName         = 'EnableFirstLogonAnimation'
                AnimationValue        = '0'
                LockRegistryPath      = 'HKLM:\Software\Policies\Microsoft\Windows\Personalization'
                LockName              = 'NoLockScreen'
                LockValue             = '1'
            }

            Disable-OOBEPrivacy @DisableOOBEPrivacyParams

        }
        Add-Step -description "Setting RunOnce script" -action { 


            # Example usage with splatting
            $SetRunOnceParams = @{
                ScriptPath      = $ScriptPath
                RunOnceKey      = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
                PowershellPath  = "C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe"
                ExecutionPolicy = "Unrestricted"
                RunOnceName     = "NextRun"
            }

            Set-RunOnce @SetRunOnceParams
        }
        Add-Step -description "Suspending BitLocker With Reboot Count" -action {

            # Example usage with splatting
            $SuspendBitLockerWithRebootParams = @{
                MountPoint  = "C:"
                RebootCount = 3
            }

            Suspend-BitLockerWithReboot @SuspendBitLockerWithRebootParams

        }
        Add-Step -description "Removing Intune management" -action { 

            # $RemoveCompanyPortalParams = @{
            # AppxPackageName = "Microsoft.CompanyPortal"
            # }

            # Remove-CompanyPortal @RemoveCompanyPortalParams

            $RemoveIntuneMgmtParams = @{
                OMADMPath             = "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts\*"
                EnrollmentBasePath    = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments"
                TrackedBasePath       = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\EnterpriseResourceManager\Tracked"
                PolicyManagerBasePath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager"
                ProvisioningBasePath  = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning"
                CertCurrentUserPath   = "cert:\CurrentUser"
                CertLocalMachinePath  = "cert:\LocalMachine"
                TaskPathBase          = "\Microsoft\Windows\EnterpriseMgmt"
                MSDMProviderID        = "MS DM Server"
                RegistryPathsToRemove = @(
                    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments",
                    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\Status",
                    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\EnterpriseResourceManager\Tracked",
                    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager\AdmxInstalled",
                    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager\Providers",
                    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts",
                    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Logger",
                    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Sessions"
                )
                UserCertIssuer        = "CN=SC_Online_Issuing"
                DeviceCertIssuers     = @("CN=Microsoft Intune Root Certification Authority", "CN=Microsoft Intune MDM Device CA")
            }

            Remove-IntuneMgmt @RemoveIntuneMgmtParams

        }
        Add-Step -description "Removing hybrid join" -action { Remove-Hybrid }
        Add-Step -description "Removing AD join" -action { 


            $RemoveADJoinParams = @{
                # DomainLeaveUser = $DomainLeaveUser
                # DomainLeavePassword = $DomainLeavePassword
                TempUser         = $TempUser
                TempUserPassword = $TempUserPassword
                ComputerName     = "localhost"
                TaskName         = "AADM Launch PSADT for Interactive Migration"
            }
            
            Remove-ADJoin @RemoveADJoinParams


        }
    }

    Process {
        try {
            foreach ($step in $global:steps) {
                Log-And-Execute-Step -Step $step
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Main-MigrateToAADJOnly function" -Level "Notice"
    }
}

# $MainMigrateParams = @{
# PPKGName = "YourProvisioningPackName"
# DomainLeaveUser = "YourDomainUser"
# DomainLeavePassword = "YourDomainPassword"
# TempUser = "YourTempUser"
# TempUserPassword = "YourTempUserPassword"
# ScriptPath = "C:\ProgramData\AADMigration\Scripts\PostRunOnce.ps1"
# }

# Main-MigrateToAADJOnly @MainMigrateParams
#EndRegion '.\Public\Main-MigrateToAADJOnly.ps1' 199
#Region '.\Public\New-MigrationTask.ps1' -1

function New-MigrationTask {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$StartBoundary,

        [Parameter(Mandatory = $true)]
        [string]$ScriptPath,

        [Parameter(Mandatory = $true)]
        [string]$TaskPath,

        [Parameter(Mandatory = $true)]
        [string]$TaskName,

        [Parameter(Mandatory = $true)]
        [string]$Description,

        [Parameter(Mandatory = $true)]
        [string]$UserId,

        [Parameter(Mandatory = $true)]
        [string]$RunLevel,

        [Parameter(Mandatory = $true)]
        [string]$Delay,

        [Parameter(Mandatory = $true)]
        [string]$ExecutePath,

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

    Begin {
        Write-EnhancedLog -Message "Starting New-MigrationTask function" -Level "INFO"
        Log-Params -Params @{
            StartBoundary = $StartBoundary
            ScriptPath    = $ScriptPath
            TaskPath      = $TaskPath
            TaskName      = $TaskName
            Description   = $Description
            UserId        = $UserId
            RunLevel      = $RunLevel
            Delay         = $Delay
            ExecutePath   = $ExecutePath
            Arguments     = $Arguments
        }
    }

    Process {
        try {
            $action = New-ScheduledTaskAction -Execute $ExecutePath -Argument $Arguments

            $trigger = New-ScheduledTaskTrigger -AtLogOn 
            $trigger.Delay = $Delay
            $trigger.StartBoundary = $StartBoundary

            $principal = New-ScheduledTaskPrincipal -UserId $UserId -RunLevel $RunLevel

            Register-ScheduledTask -Principal $principal -Action $action -Trigger $trigger -TaskName $TaskName -Description $Description -TaskPath $TaskPath
        } catch {
            Write-EnhancedLog -Message "An error occurred while creating the migration task: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting New-MigrationTask function" -Level "INFO"
    }
}

# Define parameters
# $MigrationTaskParams = @{
# StartBoundary = "2024-07-11T12:00:00"
# ScriptPath = "C:\ProgramData\AADMigration\Scripts\Launch-DeployApplication_SchTask.ps1"
# TaskPath = "AAD Migration"
# TaskName = "AADM Launch PSADT for Interactive Migration"
# Description = "AADM Launch PSADT for Interactive Migration"
# UserId = "SYSTEM"
# RunLevel = "Highest"
# Delay = "PT1M"
# ExecutePath = "PowerShell.exe"
# Arguments = "-executionpolicy Bypass -file `"$ScriptPath`""
# }

# # Example usage with splatting
# New-MigrationTask @MigrationTaskParams
#EndRegion '.\Public\New-MigrationTask.ps1' 89
#Region '.\Public\PostRunOnce.ps1' -1

# # Example usage
# $params = @{
# MigrationConfigPath = "C:\ProgramData\AADMigration\scripts\MigrationConfig.psd1"
# ImagePath = "C:\ProgramData\AADMigration\Files\MigrationInProgress.bmp"
# RunOnceScriptPath = "C:\ProgramData\AADMigration\Scripts\PostRunOnce2.ps1"
# RunOnceKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
# PowershellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe"
# ExecutionPolicy = "Unrestricted"
# RunOnceName = "NextRun"
# }
# Start-MigrationProcess @params
#EndRegion '.\Public\PostRunOnce.ps1' 12
#Region '.\Public\PostRunOnce2.ps1' -1


function PostRunOnce2 {
    <#
    .SYNOPSIS
    Executes post-run operations for the second phase of the migration process.
 
    .DESCRIPTION
    The PostRunOnce2 function blocks user input, displays a migration in progress form, creates a scheduled task for post-migration cleanup, escrows the BitLocker recovery key, sets various registry values, and restarts the computer.
 
    .PARAMETER ImagePath
    The path to the image file to be displayed on the migration progress form.
 
    .PARAMETER TaskPath
    The path of the task in Task Scheduler.
 
    .PARAMETER TaskName
    The name of the scheduled task.
 
    .PARAMETER ScriptPath
    The path to the PowerShell script to be executed by the scheduled task.
 
    .PARAMETER BitlockerDrives
    An array of drive letters for the BitLocker protected drives.
 
    .PARAMETER RegistrySettings
    A hashtable of registry settings to be applied.
 
    .EXAMPLE
    $params = @{
        ImagePath = "C:\ProgramData\AADMigration\Files\MigrationInProgress.bmp"
        TaskPath = "AAD Migration"
        TaskName = "Run Post-migration cleanup"
        ScriptPath = "C:\ProgramData\AADMigration\Scripts\PostRunOnce3.ps1"
        BitlockerDrives = @("C:", "D:")
        RegistrySettings = @{
            "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" = @{
                "AutoAdminLogon" = @{
                    "Type" = "DWORD"
                    "Data" = "0"
                }
            }
            "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" = @{
                "dontdisplaylastusername" = @{
                    "Type" = "DWORD"
                    "Data" = "1"
                }
                "legalnoticecaption" = @{
                    "Type" = "String"
                    "Data" = "Migration Completed"
                }
                "legalnoticetext" = @{
                    "Type" = "String"
                    "Data" = "This PC has been migrated to Azure Active Directory. Please log in to Windows using your email address and password."
                }
            }
        }
    }
    PostRunOnce2 @params
    Executes the post-run operations.
    #>


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

        [Parameter(Mandatory = $true)]
        [string]$TaskPath,

        [Parameter(Mandatory = $true)]
        [string]$TaskName,

        [Parameter(Mandatory = $true)]
        [string]$ScriptPath,

        [Parameter(Mandatory = $true)]
        [string[]]$BitlockerDrives,

        [Parameter(Mandatory = $true)]
        [hashtable]$RegistrySettings
    )

    Begin {
        Write-EnhancedLog -Message "Starting PostRunOnce2 function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Start-Transcript -Path "C:\ProgramData\AADMigration\Logs\AD2AADJ-R2.txt" -Append -Verbose

            # Block user input
            $blockParams = @{
                Block = $true
            }
            Block-UserInput @blockParams

            # Show migration in progress form
            $formParams = @{
                ImagePath = $ImagePath
            }
            Show-MigrationInProgressForm @formParams

            # Create scheduled task for post-migration cleanup
            $taskParams = @{
                TaskPath   = $TaskPath
                TaskName   = $TaskName
                ScriptPath = $ScriptPath
            }
            Create-ScheduledTask @taskParams

            # $schedulerconfigPath = Join-Path -Path $PSScriptRoot -ChildPath "config.psd1"
            # $taskParams = @{
            # ConfigPath = $schedulerconfigPath
            # FileName = "run-ps-hidden.vbs"
            # Scriptroot = $PSScriptRoot
            # }

            # CreateAndExecuteScheduledTask @taskParams


            # Escrow BitLocker recovery key for each drive
            foreach ($drive in $BitlockerDrives) {
                $escrowParams = @{
                    DriveLetter = $drive
                }
                Escrow-BitLockerKey @escrowParams
            }

            # Set registry values
            foreach ($regPath in $RegistrySettings.Keys) {
                foreach ($regName in $RegistrySettings[$regPath].Keys) {
                    $regSetting = $RegistrySettings[$regPath][$regName]
                    $regParams = @{
                        RegKeyPath = $regPath
                        RegValName = $regName
                        RegValType = $regSetting["Type"]
                        RegValData = $regSetting["Data"]
                    }
                    Set-RegistryValue @regParams
                }
            }

            # Stop-Transcript

            # Unblock user input and close form
            Block-UserInput -Block $false

            Restart-Computer
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in PostRunOnce2 function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting PostRunOnce2 function" -Level "Notice"
    }
}

# Example usage
# $params = @{
# ImagePath = "C:\ProgramData\AADMigration\Files\MigrationInProgress.bmp"
# TaskPath = "AAD Migration"
# TaskName = "Run Post-migration cleanup"
# ScriptPath = "C:\ProgramData\AADMigration\Scripts\PostRunOnce3.ps1"
# BitlockerDrives = @("C:", "D:")
# RegistrySettings = @{
# "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" = @{
# "AutoAdminLogon" = @{
# "Type" = "DWORD"
# "Data" = "0"
# }
# }
# "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" = @{
# "dontdisplaylastusername" = @{
# "Type" = "DWORD"
# "Data" = "1"
# }
# "legalnoticecaption" = @{
# "Type" = "String"
# "Data" = "Migration Completed"
# }
# "legalnoticetext" = @{
# "Type" = "String"
# "Data" = "This PC has been migrated to Azure Active Directory. Please log in to Windows using your email address and password."
# }
# }
# }
# }
# PostRunOnce2 @params

# Example usage
# PostRunOnce2
#EndRegion '.\Public\PostRunOnce2.ps1' 196
#Region '.\Public\PostRunOnce3.ps1' -1

function PostRunOnce3 {
  <#
  .SYNOPSIS
  Executes post-run operations for the third phase of the migration process.
 
  .DESCRIPTION
  The PostRunOnce3 function performs cleanup tasks after migration, including removing temporary user accounts, disabling local user accounts, removing scheduled tasks, clearing OneDrive cache, and setting registry values.
 
  .PARAMETER TempUser
  The name of the temporary user account to be removed.
 
  .PARAMETER RegistrySettings
  A hashtable of registry settings to be applied.
 
  .PARAMETER MigrationDirectories
  An array of directories to be removed as part of migration cleanup.
 
  .EXAMPLE
  $params = @{
      TempUser = "TempUser"
      RegistrySettings = @{
          "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" = @{
              "dontdisplaylastusername" = @{
                  "Type" = "DWORD"
                  "Data" = "0"
              }
              "legalnoticecaption" = @{
                  "Type" = "String"
                  "Data" = $null
              }
              "legalnoticetext" = @{
                  "Type" = "String"
                  "Data" = $null
              }
          }
          "HKLM:\Software\Policies\Microsoft\Windows\Personalization" = @{
              "NoLockScreen" = @{
                  "Type" = "DWORD"
                  "Data" = "0"
              }
          }
      }
      MigrationDirectories = @(
          "C:\ProgramData\AADMigration\Files",
          "C:\ProgramData\AADMigration\Scripts",
          "C:\ProgramData\AADMigration\Toolkit"
      )
  }
  PostRunOnce3 @params
  Executes the post-run operations.
  #>


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

      [Parameter(Mandatory = $true)]
      [hashtable]$RegistrySettings,

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

  Begin {
      Write-EnhancedLog -Message "Starting PostRunOnce3 function" -Level "Notice"
      Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
  }

  Process {
      try {
          Start-Transcript -Path "C:\ProgramData\AADMigration\Logs\AD2AADJ-R3.txt" -Append -Force

          # Remove temporary user account
          $removeUserParams = @{
              UserName = $TempUser
          }
          Remove-LocalUserAccount @removeUserParams

          # Disable local user accounts
          Disable-LocalUserAccounts

          # Set registry values
          foreach ($regPath in $RegistrySettings.Keys) {
              foreach ($regName in $RegistrySettings[$regPath].Keys) {
                  $regSetting = $RegistrySettings[$regPath][$regName]
                  $regParams = @{
                      RegKeyPath = $regPath
                      RegValName = $regName
                      RegValType = $regSetting["Type"]
                      RegValData = $regSetting["Data"]
                  }
                  Set-RegistryValue @regParams
              }
          }

          # Remove scheduled tasks
          $taskParams = @{
              TaskPath = "AAD Migration"
          }
          Remove-ScheduledTasks @taskParams

          # Remove migration files
          $removeFilesParams = @{
              Directories = $MigrationDirectories
          }
          Remove-MigrationFiles @removeFilesParams

          # Clear OneDrive cache
          Clear-OneDriveCache

          Stop-Transcript
      }
      catch {
          Write-EnhancedLog -Message "An error occurred in PostRunOnce3 function: $($_.Exception.Message)" -Level "ERROR"
          Handle-Error -ErrorRecord $_
      }
  }

  End {
      Write-EnhancedLog -Message "Exiting PostRunOnce3 function" -Level "Notice"
  }
}

# Example usage
# $PostRunOnce3params = @{
# TempUser = "TempUser"
# RegistrySettings = @{
# "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" = @{
# "dontdisplaylastusername" = @{
# "Type" = "DWORD"
# "Data" = "0"
# }
# "legalnoticecaption" = @{
# "Type" = "String"
# "Data" = $null
# }
# "legalnoticetext" = @{
# "Type" = "String"
# "Data" = $null
# }
# }
# "HKLM:\Software\Policies\Microsoft\Windows\Personalization" = @{
# "NoLockScreen" = @{
# "Type" = "DWORD"
# "Data" = "0"
# }
# }
# }
# MigrationDirectories = @(
# "C:\ProgramData\AADMigration\Files",
# "C:\ProgramData\AADMigration\Scripts",
# "C:\ProgramData\AADMigration\Toolkit"
# )
# }
# PostRunOnce3 @PostRunOnce3params
#EndRegion '.\Public\PostRunOnce3.ps1' 157
#Region '.\Public\Prepare-AADMigration.ps1' -1

function Prepare-AADMigration {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$MigrationPath,

        [Parameter(Mandatory = $true)]
        [string]$PSScriptbase,

        [Parameter(Mandatory = $true)]
        [string]$ConfigBaseDirectory,

        [Parameter(Mandatory = $true)]
        [string]$ConfigFileName,

        [Parameter(Mandatory = $true)]
        [string]$TenantID,

        [Parameter(Mandatory = $true)]
        [bool]$OneDriveKFM,

        [Parameter(Mandatory = $true)]
        [bool]$InstallOneDrive
    )

    Begin {
        Write-EnhancedLog -Message "Starting Prepare-AADMigration function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
        # Log-Params -Params @{
        # MigrationPath = $MigrationPath
        # PSScriptbase = $PSScriptbase
        # ConfigBaseDirectory = $ConfigBaseDirectory
        # ConfigFileName = $ConfigFileName
        # TenantID = $TenantID
        # OneDriveKFM = $OneDriveKFM
        # InstallOneDrive = $InstallOneDrive
        # }

       
    }

    Process {
        try {
            # Ensure the target directory exists
            if (-not (Test-Path -Path $MigrationPath)) {
                New-Item -Path $MigrationPath -ItemType Directory -Force | Out-Null
            }

            # Copy the entire content of $PSScriptRoot to $MigrationPath
      
            # Define the source and destination paths
            $sourcePath1 = $PSScriptbase
            $sourcePath2 = "C:\code\modulesv2"
            $destinationPath1 = $MigrationPath
            $destinationPath2 = "$MigrationPath\modulesv2"

            # Copy files from $PSScriptRoot using the Copy-FilesToPath function
            # Copy-FilesToPath -SourcePath $sourcePath1 -DestinationPath $destinationPath1

            Stop-ProcessesUsingOneDriveLib -OneDriveLibPath "C:\ProgramData\AADMigration\Files\OneDriveLib.dll"

            # $DBG

            Remove-ScheduledTaskFilesWithLogging -Path $destinationPath1

            # Copy-FilesToPathWithKill -SourcePath $sourcePath1 -DestinationPath $destinationPath1

            # Ensure the destination directory exists
            if (-not (Test-Path -Path $destinationPath1)) {
                New-Item -Path $destinationPath1 -ItemType Directory | Out-Null
            }


            $params = @{
                Source          = $sourcePath1
                Destination     = $destinationPath1
                Exclude         = ".git"
                RetryCount      = 2
                WaitTime        = 5
                RequiredSpaceGB = 10
            }


            # Execute the function with splatting
            Copy-FilesWithRobocopy @params


            # Verify the copy operation for $PSScriptRoot
            Verify-CopyOperation -SourcePath $sourcePath1 -DestinationPath $destinationPath1





            ####################################################################################

            # Copy files from C:\code\modules using the Copy-FilesToPath function
            # Copy-FilesToPath -SourcePath $sourcePath2 -DestinationPath $destinationPath2

            # Copy-FilesToPathWithKill -SourcePath $sourcePath2 -DestinationPath $destinationPath2


            # Ensure the destination directory exists
            if (-not (Test-Path -Path $destinationPath2)) {
                New-Item -Path $destinationPath2 -ItemType Directory | Out-Null
            }


            $params = @{
                Source          = $sourcePath2
                Destination     = $destinationPath2
                Exclude         = ".git"
                RetryCount      = 2
                WaitTime        = 5
                RequiredSpaceGB = 10
            }

            # Execute the function with splatting
            Copy-FilesWithRobocopy @params




            # Verify the copy operation for C:\code\modules
            Verify-CopyOperation -SourcePath $sourcePath2 -DestinationPath $destinationPath2


            # $DBG

            Write-EnhancedLog -Message "Copied content from $PSScriptRoot to $MigrationPath" -Level "INFO"

            # $DBG

            # Import migration configuration
            $MigrationConfig = Import-LocalizedData -BaseDirectory $ConfigBaseDirectory -FileName $ConfigFileName
            $TenantID = $MigrationConfig.TenantID
            $OneDriveKFM = $MigrationConfig.UseOneDriveKFM
            $InstallOneDrive = $MigrationConfig.InstallOneDrive

            # $DBG

            # Set OneDrive KFM settings if required
            if ($OneDriveKFM) {

                # $TenantID = "YourTenantID"
                $RegistrySettings = @(
                    @{
                        RegValName = "AllowTenantList"
                        RegValType = "STRING"
                        RegValData = $TenantID
                    },
                    @{
                        RegValName = "SilentAccountConfig"
                        RegValType = "DWORD"
                        RegValData = "1"
                    },
                    @{
                        RegValName = "KFMOptInWithWizard"
                        RegValType = "STRING"
                        RegValData = $TenantID
                    },
                    @{
                        RegValName = "KFMSilentOptIn"
                        RegValType = "STRING"
                        RegValData = $TenantID
                    },
                    @{
                        RegValName = "KFMSilentOptInDesktop"
                        RegValType = "DWORD"
                        RegValData = "1"
                    },
                    @{
                        RegValName = "KFMSilentOptInDocuments"
                        RegValType = "DWORD"
                        RegValData = "1"
                    },
                    @{
                        RegValName = "KFMSilentOptInPictures"
                        RegValType = "DWORD"
                        RegValData = "1"
                    }
                )
                
                $SetODKFMRegistrySettingsParams = @{
                    TenantID         = $TenantID
                    RegKeyPath       = "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive"
                    RegistrySettings = $RegistrySettings
                }
                
                Set-ODKFMRegistrySettings @SetODKFMRegistrySettingsParams
                


                $DBG

                Unregister-ScheduledTaskWithLogging -TaskName "AADM Get OneDrive Sync Status"

                # Example usage with splatting
                $CreateOneDriveSyncStatusTaskParams = @{
                    TaskPath               = "AAD Migration"
                    TaskName               = "AADM Get OneDrive Sync Status"
                    ScriptDirectory        = "C:\ProgramData\AADMigration\Scripts"
                    ScriptName             = "Check-OneDriveSyncStatus.ps1"
                    TaskArguments          = "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -file `"{ScriptPath}`""
                    TaskRepetitionDuration = "P1D"
                    TaskRepetitionInterval = "PT30M"
                    TaskPrincipalGroupId   = "BUILTIN\Users"
                    PowerShellPath         = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
                    TaskDescription        = "Get current OneDrive Sync Status and write to event log"
                }

                Create-OneDriveSyncStatusTask @CreateOneDriveSyncStatusTaskParams

                $DBG

            }

            # Install OneDrive if required
            if ($InstallOneDrive) {
                

                # Example usage
                $InstallOneDriveParams = @{
                    MigrationPath            = "C:\ProgramData\AADMigration"
                    OneDriveKFM              = $true
                    ODSetupUri               = "https://go.microsoft.com/fwlink/?linkid=844652"
                    ODSetupFile              = "Files\OneDriveSetup.exe"
                    ODRegKey                 = "HKLM:\SOFTWARE\Microsoft\OneDrive"
                    OneDriveExePath          = "C:\Program Files\Microsoft OneDrive\OneDrive.exe"
                    ScheduledTaskName        = "OneDriveRemediation"
                    ScheduledTaskDescription = "Restart OneDrive to kick off KFM sync"
                    # ScheduledTaskArgumentList = ""
                    SetupArgumentList        = "/allusers"
                }

                Install-OneDrive @InstallOneDriveParams

                $DBG



               
            }



            #Todo now we have OneDrive installed and running we need to actually start using our OneDrive for Business location on the local machine to copy user specific files into it as part of our On-prem AD to Entra ID migration prep so we need to copy the following PR4B projects from before

            # 1- copy Outlook Signatures
            # 2- copy Downloads folders
            # any other user specific files

            # Example usage
            $BackupChromeBookmarksToOneDriveParams = @{
                ChromeProfilePath = "$env:USERPROFILE\AppData\Local\Google\Chrome\User Data\Default"
                BackupFolderName  = "ChromeBackup"
                Exclude           = ".git"
                RetryCount        = 2
                WaitTime          = 5
                RequiredSpaceGB   = 10
            }
            Backup-ChromeBookmarksToOneDrive @BackupChromeBookmarksToOneDriveParams


            $BackupOutlookSignaturesToOneDrive = @{
                SignaturePath    = "$env:USERPROFILE\AppData\Roaming\Microsoft\Signatures"
                BackupFolderName = "OutlookSignatures"
                Exclude          = ".git"
                RetryCount       = 2
                WaitTime         = 5
                RequiredSpaceGB  = 10
            }
            Backup-OutlookSignaturesToOneDrive @BackupOutlookSignaturesToOneDrive


            $BackupDownloadsToOneDriveParams = @{
                DownloadsPath    = "$env:USERPROFILE\Downloads"
                BackupFolderName = "DownloadsBackup"
                Exclude          = ".git"
                RetryCount       = 2
                WaitTime         = 5
                RequiredSpaceGB  = 10
            }
            Backup-DownloadsToOneDrive @BackupDownloadsToOneDriveParams

        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Prepare-AADMigration: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Prepare-AADMigration function" -Level "Notice"
    }
}

# # Define parameters
# $PrepareAADMigrationParams = @{
# MigrationPath = "C:\ProgramData\AADMigration"
# PSScriptRoot = "C:\SourcePath"
# ConfigBaseDirectory = "C:\ConfigDirectory\Scripts"
# ConfigFileName = "MigrationConfig.psd1"
# TenantID = "YourTenantID"
# OneDriveKFM = $true
# InstallOneDrive = $true
# }

# # Example usage with splatting
# Prepare-AADMigration @PrepareAADMigrationParams
#EndRegion '.\Public\Prepare-AADMigration.ps1' 311
#Region '.\Public\Prepare-DeviceMigration-Archive.ps1' -1

# function Prepare-DeviceMigration {
# [CmdletBinding()]
# param ()

# Begin {
# Write-EnhancedLog -Message "Starting Prepare-DeviceMigration function" -Level "INFO"
# }

# Process {
# try {
# Start-Transcript -Path C:\ProgramData\AADMigration\Logs\AD2AADJ-Prep.txt -Append -Force

# $MigrationConfig = Import-LocalizedData -BaseDirectory "." -FileName "MigrationConfig.psd1"
# $MigrationPath = $MigrationConfig.MigrationPath
# $TenantID = $MigrationConfig.TenantID
# $OneDriveKFM = $MigrationConfig.UseOneDriveKFM
# $InstallOneDrive = $MigrationConfig.InstallOneDrive
# $StartBoundary = $MigrationConfig.StartBoundary

# function Add-MigrationDirectory {
# # Expand AAD Migration zip file to ProgramData
# Expand-Archive -Path "$PSScriptRoot\AADMigration.zip" -DestinationPath C:\ProgramData -Force
# Copy-Item -Path "$PSScriptRoot\MigrationConfig.psd1" -Destination "$MigrationPath\Module"
# }

# function Set-RegistryValue {
# [CmdletBinding()]
# param (
# [string]$RegKeyPath,
# [string]$RegValName,
# [string]$RegValType,
# [string]$RegValData
# )

# # Test to see if the registry key exists, if it does not, create it
# $RegKeyPathExists = Test-Path -Path $RegKeyPath
# if (-not $RegKeyPathExists) {
# New-Item -Path $RegKeyPath -Force | Out-Null
# }

# # Check to see if the value exists
# try {
# $CurrentValue = Get-ItemPropertyValue -Path $RegKeyPath -Name $RegValName
# } catch {
# # If the value does not exist, catch the error and create the key
# Set-ItemProperty -Path $RegKeyPath -Name $RegValName -Type $RegValType -Value $RegValData -Force
# }

# if ($CurrentValue -ne $RegValData) {
# # If the value exists but the data is wrong, update the value
# Set-ItemProperty -Path $RegKeyPath -Name $RegValName -Type $RegValType -Value $RegValData -Force
# }
# }

# function Set-ODKFMSettings {
# # Set registry values for enabling KFM to set tenant
# Set-RegistryValue -RegKeyPath "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" -RegValName "AllowTenantList" -RegValType "STRING" -RegValData $TenantID
# Set-RegistryValue -RegKeyPath "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" -RegValName "SilentAccountConfig" -RegValType "DWORD" -RegValData "1"
# Set-RegistryValue -RegKeyPath "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" -RegValName "KFMOptInWithWizard" -RegValType "STRING" -RegValData $TenantID
# Set-RegistryValue -RegKeyPath "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" -RegValName "KFMSilentOptIn" -RegValType "STRING" -RegValData $TenantID
# Set-RegistryValue -RegKeyPath "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" -RegValName "KFMSilentOptInDesktop" -RegValType "DWORD" -RegValData "1"
# Set-RegistryValue -RegKeyPath "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" -RegValName "KFMSilentOptInDocuments" -RegValType "DWORD" -RegValData "1"
# Set-RegistryValue -RegKeyPath "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" -RegValName "KFMSilentOptInPictures" -RegValType "DWORD" -RegValData "1"

# # Create EventLog Source
# New-EventLog -LogName 'Application' -Source 'AAD_Migration_Script' -ErrorAction Stop

# # Create scheduled task to check OneDrive sync status
# $TaskPath = "AAD Migration"
# $TaskName = "AADM Get OneDrive Sync Status"
# $ScriptPath = "C:\ProgramData\AADMigration\Scripts"
# $ScriptName = "Check-OneDriveSyncStatus.ps1"
# $arguments = "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -file $ScriptPath\$ScriptName"

# $action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument $arguments
# $trigger = New-ScheduledTaskTrigger -AtLogOn
# $trigger.Delay = 'PT1M'
# $principal = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Users"
# $Task = Register-ScheduledTask -Principal $principal -Action $action -Trigger $trigger -TaskName $TaskName -Description "Get current OneDrive Sync Status and write to event log" -TaskPath $TaskPath
# $Task.Triggers.Repetition.Duration = "P1D"
# $Task.Triggers.Repetition.Interval = "PT30M"
# $Task | Set-ScheduledTask
# }

# function Install-OneDrive {
# # Check for OneDrive machine-wide installer, check version number if it exists
# $ODSetupVersion = (Get-ChildItem -Path "$PSScriptRoot\AADMigration\Files\OneDriveSetup.exe").VersionInfo.FileVersion

# if (-not $ODSetupVersion) {
# Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/?linkid=844652" -OutFile "$PSScriptRoot\Files\OneDriveSetup.exe" -Wait
# $ODSetupVersion = (Get-ChildItem -Path "$PSScriptRoot\Files\OneDriveSetup.exe").VersionInfo.FileVersion
# }

# $ODRegKey = "HKLM:\SOFTWARE\Microsoft\OneDrive"
# $InstalledVer = Get-ItemPropertyValue -Path $ODRegKey -Name Version

# if (-not $ODRegKey -or ([System.Version]$InstalledVer -lt [System.Version]$ODSetupVersion)) {
# # Install OneDrive setup
# $Installer = "$PSScriptRoot\Files\OneDriveSetup.exe"
# $Arguments = "/allusers"
# Start-Process -FilePath $Installer -ArgumentList $Arguments
# } elseif ($OneDriveKFM) {
# # If OneDrive is already installed, stop the process and restart to kick off KFM sync if required
# Get-Process -Name OneDrive | Stop-Process -Confirm:$false -Force
# Start-Sleep -Seconds 5

# $action = New-ScheduledTaskAction -Execute "C:\Program Files\Microsoft Onedrive\OneDrive.exe"
# $trigger = New-ScheduledTaskTrigger -AtLogOn
# $principal = New-ScheduledTaskPrincipal -UserId (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty UserName)
# $task = New-ScheduledTask -Action $action -Trigger $trigger -Principal $principal
# Register-ScheduledTask -TaskName OneDriveRemediation -InputObject $task
# Start-ScheduledTask -TaskName OneDriveRemediation
# Start-Sleep -Seconds 5
# Unregister-ScheduledTask -TaskName OneDriveRemediation -Confirm:$false
# }
# }

# function New-MigrationTask {
# # Create Scheduled task to launch interactive migration task
# $TaskPath = "AAD Migration"
# $TaskName = "AADM Launch PSADT for Interactive Migration"
# $ScriptPath = "C:\ProgramData\AADMigration\Scripts"
# $ScriptName = "Launch-DeployApplication_SchTask.ps1"
# $arguments = "-executionpolicy Bypass -file $ScriptPath\$ScriptName"

# $action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument $arguments
# $trigger = New-ScheduledTaskTrigger -AtLogOn
# $trigger.Delay = 'PT1M'
# $trigger.StartBoundary = $StartBoundary
# $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
# $Task = Register-ScheduledTask -Principal $principal -Action $action -Trigger $trigger -TaskName $TaskName -Description "AADM Launch PSADT for Interactive Migration" -TaskPath $TaskPath
# }

# Add-MigrationDirectory
# New-MigrationTask

# if ($OneDriveKFM) {
# Set-ODKFMSettings
# }

# if ($InstallOneDrive) {
# Install-OneDrive
# }

# Stop-Transcript
# } catch {
# Write-EnhancedLog -Message "An error occurred in Prepare-DeviceMigration: $($_.Exception.Message)" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Prepare-DeviceMigration function" -Level "INFO"
# }
# }

# # Example usage
# # Prepare-DeviceMigration
#EndRegion '.\Public\Prepare-DeviceMigration-Archive.ps1' 159
#Region '.\Public\Prepare-SolutionDirectory.ps1' -1

function Prepare-SolutionDirectory {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ToolkitFolder,

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

    Begin {
        Write-EnhancedLog -Message "Starting Prepare-SolutionDirectory function" -Level "INFO"
        Log-Params -Params @{
            ToolkitFolder = $ToolkitFolder
            FilesFolder = $FilesFolder
        }
    }

    Process {
        try {
            # Create necessary directories
            New-Item -ItemType Directory -Path $ToolkitFolder -Force
            New-Item -ItemType Directory -Path $FilesFolder -Force
        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the Prepare-SolutionDirectory function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Prepare-SolutionDirectory function" -Level "INFO"
    }
}

# Example usage
# Prepare-SolutionDirectory -ToolkitFolder "C:\path\to\toolkit" -FilesFolder "C:\path\to\files"
#EndRegion '.\Public\Prepare-SolutionDirectory.ps1' 37
#Region '.\Public\Remove-ADJoin.ps1' -1

function Remove-ADJoin {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]$DomainLeaveUser,

        [Parameter(Mandatory = $false)]
        [string]$DomainLeavePassword,

        [Parameter(Mandatory = $true)]
        [string]$TempUser,

        [Parameter(Mandatory = $true)]
        [string]$TempUserPassword,

        [Parameter(Mandatory = $true)]
        [string]$ComputerName = "localhost",

        [Parameter(Mandatory = $true)]
        [string]$TaskName = "AADM Launch PSADT for Interactive Migration"
    )

    Begin {
        Write-EnhancedLog -Message "Starting Remove-ADJoin function" -Level "INFO"
        Log-Params -Params @{
            DomainLeaveUser = $DomainLeaveUser
            DomainLeavePassword = $DomainLeavePassword
            TempUser = $TempUser
            TempUserPassword = $TempUserPassword
            ComputerName = $ComputerName
            TaskName = $TaskName
        }
    }

    Process {
        try {
            Write-EnhancedLog -Message "Checking if device is domain joined" -Level "INFO"
            $ComputerSystem = Get-WmiObject -Class Win32_ComputerSystem
            $Domain = $ComputerSystem.Domain
            $PartOfDomain = $ComputerSystem.PartOfDomain

            if ($PartOfDomain) {
                Write-EnhancedLog -Message "Computer is domain member, removing domain membership" -Level "INFO"

                if (Test-Connection -ComputerName $Domain -Count 1 -Quiet) {
                    Write-EnhancedLog -Message "Connected to domain, attempting to leave domain." -Level "INFO"

                    if ($DomainLeaveUser) {
                        $SecurePassword = ConvertTo-SecureString -String $DomainLeavePassword -AsPlainText -Force
                        $Credentials = New-Object System.Management.Automation.PSCredential($DomainLeaveUser, $SecurePassword)

                        try {
                            Remove-Computer -ComputerName $ComputerName -Credential $Credentials -Verbose -Force -ErrorAction Stop
                            Disable-ScheduledTask -TaskName $TaskName
                            Stop-Transcript
                            Restart-Computer
                        } catch {
                            Write-EnhancedLog -Message "Leaving domain with domain credentials failed. Will leave domain with local account." -Level "ERROR"
                        }
                    }

                    $SecurePassword = ConvertTo-SecureString -String $TempUserPassword -AsPlainText -Force
                    $Credentials = New-Object System.Management.Automation.PSCredential($TempUser, $SecurePassword)
                    $ConnectedAdapters = Get-NetAdapter | Where-Object { $_.MediaConnectionState -eq "Connected" }

                    foreach ($Adapter in $ConnectedAdapters) {
                        Write-EnhancedLog -Message "Disabling network adapter $($Adapter.Name)" -Level "INFO"
                        Disable-NetAdapter -Name $Adapter.Name -Confirm:$false
                    }

                    Start-Sleep -Seconds 5
                    Remove-Computer -ComputerName $ComputerName -Credential $Credentials -Verbose -Force

                    foreach ($Adapter in $ConnectedAdapters) {
                        Write-EnhancedLog -Message "Enabling network adapter $($Adapter.Name)" -Level "INFO"
                        Enable-NetAdapter -Name $Adapter.Name -Confirm:$false
                    }

                    Start-Sleep -Seconds 15
                    Write-EnhancedLog -Message "Computer removed from domain. Network adapters re-enabled. Restarting." -Level "INFO"
                    Disable-ScheduledTask -TaskName $TaskName
                    Stop-Transcript
                    Restart-Computer
                } else {
                    Write-Verbose "Removing computer from domain and forcing restart"
                    Write-EnhancedLog -Message "Stopping transcript and calling Remove-Computer with -Restart switch." -Level "INFO"
                    Stop-Transcript
                    Remove-Computer -ComputerName $ComputerName -Credential $Credentials -Verbose -Force -ErrorAction Stop
                    Disable-ScheduledTask -TaskName $TaskName
                    Stop-Transcript
                    Restart-Computer
                }
            } else {
                Write-EnhancedLog -Message "Computer is not a domain member, restarting computer." -Level "INFO"
                Disable-ScheduledTask -TaskName $TaskName
                Stop-Transcript
                Restart-Computer
            }
        } catch {
            Write-EnhancedLog -Message "An error occurred while removing AD join: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Remove-ADJoin function" -Level "INFO"
    }
}


# $RemoveADJoinParams = @{
# DomainLeaveUser = "YourDomainUser"
# DomainLeavePassword = "YourDomainPassword"
# TempUser = "YourTempUser"
# TempUserPassword = "YourTempUserPassword"
# ComputerName = "localhost"
# TaskName = "AADM Launch PSADT for Interactive Migration"
# }

# Remove-ADJoin @RemoveADJoinParams
#EndRegion '.\Public\Remove-ADJoin.ps1' 121
#Region '.\Public\Remove-CompanyPortal.ps1' -1

function Remove-CompanyPortal {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$AppxPackageName
    )

    Begin {
        Write-EnhancedLog -Message "Starting Remove-CompanyPortal function" -Level "INFO"
        Log-Params -Params @{ AppxPackageName = $AppxPackageName }
    }

    Process {
        try {
            Write-EnhancedLog -Message "Removing AppxPackage: $AppxPackageName" -Level "INFO"
            Get-AppxPackage -AllUsers -Name $AppxPackageName | Remove-AppxPackage -Confirm:$false
        } catch {
            Write-EnhancedLog -Message "An error occurred while removing AppxPackage: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Remove-CompanyPortal function" -Level "INFO"
    }
}

# $RemoveCompanyPortalParams = @{
# AppxPackageName = "Microsoft.CompanyPortal"
# }

# Remove-CompanyPortal @RemoveCompanyPortalParams
#EndRegion '.\Public\Remove-CompanyPortal.ps1' 33
#Region '.\Public\Remove-Hybrid.ps1' -1

function Remove-Hybrid {
    [CmdletBinding()]
    param ()

    Begin {
        Write-EnhancedLog -Message "Starting Remove-Hybrid function" -Level "INFO"
    }

    Process {
        try {
            Write-EnhancedLog -Message "Checking if device is Azure AD joined" -Level "INFO"
            $Dsregcmd = New-Object PSObject
            Dsregcmd /status | Where-Object { $_ -match ' : ' } | ForEach-Object {
                $Item = $_.Trim() -split '\s:\s'
                $Dsregcmd | Add-Member -MemberType NoteProperty -Name $($Item[0] -replace '[:\s]', '') -Value $Item[1] -ErrorAction SilentlyContinue
            }

            $AzureADJoined = $Dsregcmd.AzureAdJoined

            if ($AzureADJoined -eq 'Yes') {
                Write-EnhancedLog -Message "Device is Azure AD joined. Removing hybrid join." -Level "INFO"
                & "C:\Windows\System32\dsregcmd.exe" /leave
            }
        } catch {
            Write-EnhancedLog -Message "An error occurred while removing hybrid join: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Remove-Hybrid function" -Level "INFO"
    }
}

# Example usage
# Remove-Hybrid
#EndRegion '.\Public\Remove-Hybrid.ps1' 37
#Region '.\Public\Remove-IntuneMgmt.ps1' -1

function Remove-IntuneMgmt {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$OMADMPath,

        [Parameter(Mandatory = $true)]
        [string]$EnrollmentBasePath,

        [Parameter(Mandatory = $true)]
        [string]$TrackedBasePath,

        [Parameter(Mandatory = $true)]
        [string]$PolicyManagerBasePath,

        [Parameter(Mandatory = $true)]
        [string]$ProvisioningBasePath,

        [Parameter(Mandatory = $true)]
        [string]$CertCurrentUserPath,

        [Parameter(Mandatory = $true)]
        [string]$CertLocalMachinePath,

        [Parameter(Mandatory = $true)]
        [string]$TaskPathBase,

        [Parameter(Mandatory = $true)]
        [string]$MSDMProviderID,

        [Parameter(Mandatory = $true)]
        [string[]]$RegistryPathsToRemove,

        [Parameter(Mandatory = $true)]
        [string]$UserCertIssuer,

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

    Begin {
        Write-EnhancedLog -Message "Starting Remove-IntuneMgmt function" -Level "INFO"
        Log-Params -Params @{
            OMADMPath              = $OMADMPath
            EnrollmentBasePath     = $EnrollmentBasePath
            TrackedBasePath        = $TrackedBasePath
            PolicyManagerBasePath  = $PolicyManagerBasePath
            ProvisioningBasePath   = $ProvisioningBasePath
            CertCurrentUserPath    = $CertCurrentUserPath
            CertLocalMachinePath   = $CertLocalMachinePath
            TaskPathBase           = $TaskPathBase
            MSDMProviderID         = $MSDMProviderID
            RegistryPathsToRemove  = $RegistryPathsToRemove
            UserCertIssuer         = $UserCertIssuer
            DeviceCertIssuers      = $DeviceCertIssuers
        }
    }

    Process {
        try {
            Write-EnhancedLog -Message "Checking Intune enrollment status" -Level "INFO"
            $Account = (Get-ItemProperty -Path $OMADMPath -ErrorAction SilentlyContinue).PSChildName

            $Enrolled = $true
            $EnrollmentPath = "$EnrollmentBasePath\$Account"
            $EnrollmentUPN = (Get-ItemProperty -Path $EnrollmentPath -ErrorAction SilentlyContinue).UPN
            $ProviderID = (Get-ItemProperty -Path $EnrollmentPath -ErrorAction SilentlyContinue).ProviderID

            if (-not $EnrollmentUPN -or $ProviderID -ne $MSDMProviderID) {
                $Enrolled = $false
            }

            if ($Enrolled) {
                Write-EnhancedLog -Message "Device is enrolled in Intune. Proceeding with unenrollment." -Level "INFO"

                # Delete Task Schedule tasks
                Get-ScheduledTask -TaskPath "$TaskPathBase\$Account\*" | Unregister-ScheduledTask -Confirm:$false -ErrorAction SilentlyContinue

                # Delete registry keys
                foreach ($RegistryPath in $RegistryPathsToRemove) {
                    Remove-Item -Path "$RegistryPath\$Account" -Recurse -Force -ErrorAction SilentlyContinue
                }

                # Delete enrollment certificates
                $UserCerts = Get-ChildItem -Path $CertCurrentUserPath -Recurse
                $IntuneCerts = $UserCerts | Where-Object { $_.Issuer -eq $UserCertIssuer }
                foreach ($Cert in $IntuneCerts) {
                    $Cert | Remove-Item -Force
                }
                $DeviceCerts = Get-ChildItem -Path $CertLocalMachinePath -Recurse
                $IntuneCerts = $DeviceCerts | Where-Object { $DeviceCertIssuers -contains $_.Issuer }
                foreach ($Cert in $IntuneCerts) {
                    $Cert | Remove-Item -Force -ErrorAction SilentlyContinue
                }
            }
        } catch {
            Write-EnhancedLog -Message "An error occurred while removing Intune management: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Remove-IntuneMgmt function" -Level "INFO"
    }
}


# $RemoveIntuneMgmtParams = @{
# OMADMPath = "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts\*"
# EnrollmentBasePath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments"
# TrackedBasePath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\EnterpriseResourceManager\Tracked"
# PolicyManagerBasePath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager"
# ProvisioningBasePath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning"
# CertCurrentUserPath = "cert:\CurrentUser"
# CertLocalMachinePath = "cert:\LocalMachine"
# TaskPathBase = "\Microsoft\Windows\EnterpriseMgmt"
# MSDMProviderID = "MS DM Server"
# RegistryPathsToRemove = @(
# "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments",
# "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\Status",
# "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\EnterpriseResourceManager\Tracked",
# "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager\AdmxInstalled",
# "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager\Providers",
# "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts",
# "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Logger",
# "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Sessions"
# )
# UserCertIssuer = "CN=SC_Online_Issuing"
# DeviceCertIssuers = @("CN=Microsoft Intune Root Certification Authority", "CN=Microsoft Intune MDM Device CA")
# }

# Remove-IntuneMgmt @RemoveIntuneMgmtParams
#EndRegion '.\Public\Remove-IntuneMgmt.ps1' 133
#Region '.\Public\Remove-LocalUserAccount.ps1' -1

function Remove-LocalUserAccount {
    <#
    .SYNOPSIS
    Removes a local user account.
   
    .DESCRIPTION
    The Remove-LocalUserAccount function removes a specified local user account.
   
    .PARAMETER UserName
    The name of the local user account to be removed.
   
    .EXAMPLE
    $params = @{
        UserName = "TempUser"
    }
    Remove-LocalUserAccount @params
    Removes the local user account named TempUser.
    #>

  
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$UserName
    )
  
    Begin {
        Write-EnhancedLog -Message "Starting Remove-LocalUserAccount function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }
  
    Process {
        try {
            Write-EnhancedLog -Message "Removing local user account: $UserName" -Level "INFO"
            Remove-LocalUser -Name $UserName -ErrorAction Stop
            Write-EnhancedLog -Message "Successfully removed local user account: $UserName" -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Remove-LocalUserAccount function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }
  
    End {
        Write-EnhancedLog -Message "Exiting Remove-LocalUserAccount function" -Level "Notice"
    }
  }
  
  # # Example usage
  # $params = @{
  # UserName = "TempUser"
  # }
  # Remove-LocalUserAccount @params
#EndRegion '.\Public\Remove-LocalUserAccount.ps1' 54
#Region '.\Public\Remove-MigrationFiles.ps1' -1


function Remove-MigrationFiles {
    <#
    .SYNOPSIS
    Removes specified directories used during the migration process.
   
    .DESCRIPTION
    The Remove-MigrationFiles function deletes specified directories used during the migration process, leaving the log folder intact.
   
    .PARAMETER Directories
    An array of directories to be removed.
   
    .EXAMPLE
    $params = @{
        Directories = @(
            "C:\ProgramData\AADMigration\Files",
            "C:\ProgramData\AADMigration\Scripts",
            "C:\ProgramData\AADMigration\Toolkit"
        )
    }
    Remove-MigrationFiles @params
    Removes the specified directories.
    #>

  
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string[]]$Directories
    )
  
    Begin {
        Write-EnhancedLog -Message "Starting Remove-MigrationFiles function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }
  
    Process {
        try {
            foreach ($directory in $Directories) {
                if (Test-Path -Path $directory) {
                    Write-EnhancedLog -Message "Removing directory: $directory" -Level "INFO"
                    Remove-Item -Path $directory -Recurse -Force -ErrorAction Stop
                    Write-EnhancedLog -Message "Successfully removed directory: $directory" -Level "INFO"
                }
                else {
                    Write-EnhancedLog -Message "Directory not found: $directory" -Level "WARNING"
                }
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Remove-MigrationFiles function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }
  
    End {
        Write-EnhancedLog -Message "Exiting Remove-MigrationFiles function" -Level "Notice"
    }
  }
  
  # # Example usage
  # $params = @{
  # Directories = @(
  # "C:\ProgramData\AADMigration\Files",
  # "C:\ProgramData\AADMigration\Scripts",
  # "C:\ProgramData\AADMigration\Toolkit"
  # )
  # }
  # Remove-MigrationFiles @params
#EndRegion '.\Public\Remove-MigrationFiles.ps1' 70
#Region '.\Public\Remove-ScheduledTasks.ps1' -1

function Remove-ScheduledTasks {
    <#
    .SYNOPSIS
    Removes scheduled tasks created for the migration.
   
    .DESCRIPTION
    The Remove-ScheduledTasks function removes all scheduled tasks under a specified task path.
   
    .PARAMETER TaskPath
    The path of the task in Task Scheduler.
   
    .EXAMPLE
    $params = @{
        TaskPath = "AAD Migration"
    }
    Remove-ScheduledTasks @params
    Removes all scheduled tasks under the "AAD Migration" task path.
    #>

  
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$TaskPath
    )
  
    Begin {
        Write-EnhancedLog -Message "Starting Remove-ScheduledTasks function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }
  
    Process {
        try {
            Write-EnhancedLog -Message "Removing scheduled tasks under task path: $TaskPath" -Level "INFO"
            $tasks = Get-ScheduledTask -TaskPath "\$TaskPath\"
            foreach ($task in $tasks) {
                Unregister-ScheduledTask -TaskName $task.TaskName -Confirm:$false -ErrorAction Stop
                Write-EnhancedLog -Message "Successfully removed scheduled task: $($task.TaskName)" -Level "INFO"
            }
            $scheduler = New-Object -ComObject "Schedule.Service"
            $scheduler.Connect()
            $rootFolder = $scheduler.GetFolder("\")
            $rootFolder.DeleteFolder($TaskPath, $null)
            Write-EnhancedLog -Message "Successfully removed task folder: $TaskPath" -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Remove-ScheduledTasks function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }
  
    End {
        Write-EnhancedLog -Message "Exiting Remove-ScheduledTasks function" -Level "Notice"
    }
  }
  
  # # Example usage
  # $params = @{
  # TaskPath = "AAD Migration"
  # }
  # Remove-ScheduledTasks @params
#EndRegion '.\Public\Remove-ScheduledTasks.ps1' 62
#Region '.\Public\Replace-BannerImage.ps1' -1

function Replace-BannerImage {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Source,

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

    Begin {
        Write-EnhancedLog -Message "Starting Replace-BannerImage function" -Level "INFO"
        Log-Params -Params @{
            Source = $Source
            Destination = $Destination
        }
    }

    Process {
        try {
            # Replace the banner image in the toolkit folder
            Copy-Item -Path $Source -Destination $Destination -Force
        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the Replace-BannerImage function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Replace-BannerImage function" -Level "INFO"
    }
}

# Example usage
# Replace-BannerImage -Source 'C:\YourPath\YourBannerImage.png' -Destination 'C:\YourPath\Toolkit\AppDeployToolkit\AppDeployToolkitBanner.png'
#EndRegion '.\Public\Replace-BannerImage.ps1' 36
#Region '.\Public\Replace-DeployApplicationPS1.ps1' -1

function Replace-DeployApplicationPS1 {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Source,

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

    Begin {
        Write-EnhancedLog -Message "Starting Replace-DeployApplicationPS1 function" -Level "INFO"
        Log-Params -Params @{
            Source = $Source
            Destination = $Destination
        }
    }

    Process {
        try {
            # Replace Deploy-Application.ps1 in the toolkit folder
            Copy-Item -Path $Source -Destination $Destination -Force
        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the Replace-DeployApplicationPS1 function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Replace-DeployApplicationPS1 function" -Level "INFO"
    }
}

# Example usage
# Replace-DeployApplicationPS1 -Source 'C:\YourPath\Scripts\Deploy-Application.ps1' -Destination 'C:\YourPath\Toolkit\Deploy-Application.ps1'
#EndRegion '.\Public\Replace-DeployApplicationPS1.ps1' 36
#Region '.\Public\Set-Autologin.ps1' -1

function Set-Autologin {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$TempUser,

        [Parameter(Mandatory = $true)]
        [string]$TempUserPassword,

        [Parameter(Mandatory = $true)]
        [string]$RegPath,

        [Parameter(Mandatory = $true)]
        [string]$AutoAdminLogonName,

        [Parameter(Mandatory = $true)]
        [string]$AutoAdminLogonValue,

        [Parameter(Mandatory = $true)]
        [string]$DefaultUsernameName,

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

    Begin {
        Write-EnhancedLog -Message "Starting Set-Autologin function" -Level "INFO"
        Log-Params -Params @{
            TempUser           = $TempUser
            TempUserPassword   = $TempUserPassword
            RegPath            = $RegPath
            AutoAdminLogonName = $AutoAdminLogonName
            AutoAdminLogonValue = $AutoAdminLogonValue
            DefaultUsernameName = $DefaultUsernameName
            DefaultPasswordName = $DefaultPasswordName
        }
    }

    Process {
        try {
            Write-EnhancedLog -Message "Setting user account to Auto Login" -Level "INFO"
            Set-ItemProperty -Path $RegPath -Name $AutoAdminLogonName -Value $AutoAdminLogonValue -Type String -Verbose
            Set-ItemProperty -Path $RegPath -Name $DefaultUsernameName -Value $TempUser -Type String -Verbose
            Set-ItemProperty -Path $RegPath -Name $DefaultPasswordName -Value $TempUserPassword -Type String -Verbose
        } catch {
            Write-EnhancedLog -Message "An error occurred while setting autologin: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Set-Autologin function" -Level "INFO"
    }
}

# # Example usage with splatting
# $SetAutologinParams = @{
# TempUser = 'YourTempUser'
# TempUserPassword = 'YourTempUserPassword'
# RegPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
# AutoAdminLogonName = 'AutoAdminLogon'
# AutoAdminLogonValue = '1'
# DefaultUsernameName = 'DefaultUsername'
# DefaultPasswordName = 'DefaultPassword'
# }

# Set-Autologin @SetAutologinParams
#EndRegion '.\Public\Set-Autologin.ps1' 68
#Region '.\Public\Set-ODKFMRegistrySettings.ps1' -1

function Set-ODKFMRegistrySettings {
    <#
    .SYNOPSIS
    Sets OneDrive Known Folder Move (KFM) registry settings.
 
    .DESCRIPTION
    The Set-ODKFMRegistrySettings function sets specified registry values for OneDrive Known Folder Move (KFM) based on provided tenant ID, registry key path, and an array of registry settings.
 
    .PARAMETER TenantID
    The tenant ID for OneDrive.
 
    .PARAMETER RegKeyPath
    The path to the registry key.
 
    .PARAMETER RegistrySettings
    An array of registry settings to be applied. Each setting should include RegValName, RegValType, and RegValData.
 
    .EXAMPLE
    $settings = @(
        @{
            RegValName = "KFMValue1"
            RegValType = "String"
            RegValData = "Value1"
        },
        @{
            RegValName = "KFMValue2"
            RegValType = "DWORD"
            RegValData = "1"
        }
    )
    $params = @{
        TenantID = "your-tenant-id"
        RegKeyPath = "HKLM:\Software\Policies\Microsoft\OneDrive"
        RegistrySettings = $settings
    }
    Set-ODKFMRegistrySettings @params
    Sets the specified OneDrive KFM registry values.
    #>


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

        [Parameter(Mandatory = $true)]
        [string]$RegKeyPath,

        [Parameter(Mandatory = $true)]
        [array]$RegistrySettings
    )

    Begin {
        Write-EnhancedLog -Message "Starting Set-ODKFMRegistrySettings function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            foreach ($setting in $RegistrySettings) {
                # Define the parameters to be splatted
                $splatParams = @{
                    RegKeyPath = $RegKeyPath
                    RegValName = $setting.RegValName
                    RegValType = $setting.RegValType
                    RegValData = $setting.RegValData
                }

                # Call the function with splatted parameters
                Set-RegistryValue @splatParams
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while setting OneDrive KFM registry values: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Set-ODKFMRegistrySettings function" -Level "Notice"
    }
}


# # Example usage
# $settings = @(
# @{
# RegValueName = "KFMValue1"
# RegValType = "String"
# RegValData = "Value1"
# },
# @{
# RegValueName = "KFMValue2"
# RegValType = "DWORD"
# RegValData = "1"
# }
# )
# $params = @{
# TenantID = "your-tenant-id"
# RegKeyPath = "HKLM:\Software\Policies\Microsoft\OneDrive"
# RegistrySettings = $settings
# }
# Set-ODKFMRegistrySettings @params


# $TenantID = "YourTenantID"

# $RegistrySettings = @(
# @{
# RegValueName = "AllowTenantList"
# RegValType = "STRING"
# RegValData = $TenantID
# },
# @{
# RegValueName = "SilentAccountConfig"
# RegValType = "DWORD"
# RegValData = "1"
# },
# @{
# RegValueName = "KFMOptInWithWizard"
# RegValType = "STRING"
# RegValData = $TenantID
# },
# @{
# RegValueName = "KFMSilentOptIn"
# RegValType = "STRING"
# RegValData = $TenantID
# },
# @{
# RegValueName = "KFMSilentOptInDesktop"
# RegValType = "DWORD"
# RegValData = "1"
# },
# @{
# RegValueName = "KFMSilentOptInDocuments"
# RegValType = "DWORD"
# RegValData = "1"
# },
# @{
# RegValueName = "KFMSilentOptInPictures"
# RegValType = "DWORD"
# RegValData = "1"
# }
# )

# $SetODKFMRegistrySettingsParams = @{
# TenantID = $TenantID
# RegKeyPath = "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive"
# RegistrySettings = $RegistrySettings
# }

# Set-ODKFMRegistrySettings @SetODKFMRegistrySettingsParams


#optionally you can create an event source here using Create-EventLogSource.ps1
#EndRegion '.\Public\Set-ODKFMRegistrySettings.ps1' 155
#Region '.\Public\Set-RegistryValue.ps1' -1

# function Set-RegistryValue {
# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [string]$RegKeyPath,
# [Parameter(Mandatory = $true)]
# [string]$RegValueName,
# [Parameter(Mandatory = $true)]
# [string]$RegValType,
# [Parameter(Mandatory = $true)]
# [string]$RegValData
# )

# Begin {
# Write-EnhancedLog -Message "Starting Set-RegistryValue function" -Level "INFO"
# Log-Params -Params @{
# RegKeyPath = $RegKeyPath
# RegValueName = $RegValueName
# RegValType = $RegValType
# RegValData = $RegValData
# }
# }

# Process {
# try {
# # Check if registry key exists, create if it does not
# if (-not (Test-Path -Path $RegKeyPath)) {
# Write-EnhancedLog -Message "Registry key path does not exist, creating: $RegKeyPath" -Level "INFO"
# New-Item -Path $RegKeyPath -Force | Out-Null
# } else {
# Write-EnhancedLog -Message "Registry key path exists: $RegKeyPath" -Level "INFO"
# }

# # Check if registry value exists and its current value
# $currentValue = $null
# try {
# $currentValue = Get-ItemPropertyValue -Path $RegKeyPath -Name $RegValueName
# } catch {
# Write-EnhancedLog -Message "Registry value not found, setting new value: $RegValueName" -Level "INFO"
# New-ItemProperty -Path $RegKeyPath -Name $RegValueName -PropertyType $RegValType -Value $RegValData -Force
# }

# # If value exists but data is incorrect, update the value
# if ($currentValue -ne $RegValData) {
# Write-EnhancedLog -Message "Updating registry value: $RegValueName with new data: $RegValData" -Level "INFO"
# Set-ItemProperty -Path $RegKeyPath -Name $RegValueName -Value $RegValData -Force
# }
# } catch {
# Write-EnhancedLog -Message "An error occurred while processing the Set-RegistryValue function: $($_.Exception.Message)" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Set-RegistryValue function" -Level "INFO"
# }
# }


# # Example usage
# # Set-RegistryValue -RegKeyPath "HKCU:\Software\MyApp" -RegValueName "MyValue" -RegValType "String" -RegValData "MyData"













function Set-RegistryValue {
    <#
    .SYNOPSIS
    Sets a registry value.
 
    .DESCRIPTION
    The Set-RegistryValue function sets a registry value at a specified registry path. It creates the registry key if it does not exist and updates the value if it differs from the provided data.
 
    .PARAMETER RegKeyPath
    The path to the registry key.
 
    .PARAMETER RegValName
    The name of the registry value.
 
    .PARAMETER RegValType
    The type of the registry value (e.g., String, DWORD).
 
    .PARAMETER RegValData
    The data to be set for the registry value.
 
    .EXAMPLE
    $params = @{
        RegKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
        RegValName = "AutoAdminLogon"
        RegValType = "DWORD"
        RegValData = "0"
    }
    Set-RegistryValue @params
    Sets the AutoAdminLogon registry value to 0.
    #>


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

        [Parameter(Mandatory = $true)]
        [string]$RegValName,

        [Parameter(Mandatory = $true)]
        [string]$RegValType,

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

    Begin {
        Write-EnhancedLog -Message "Starting Set-RegistryValue function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Check if running as administrator
        # if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        # Write-EnhancedLog -Message "Script is not running as administrator. Attempting to elevate." -Level "INFO"
        # Start-Process -FilePath "powershell" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`"" -Verb RunAs
        # Exit
        # }

        CheckAndElevate
    }

    Process {
        try {
            # Test to see if registry key exists, if it does not exist create it
            if (-not (Test-Path -Path $RegKeyPath)) {
                New-Item -Path $RegKeyPath -Force | Out-Null
                Write-EnhancedLog -Message "Created registry key: $RegKeyPath" -Level "INFO"
            }

            # Check if value exists and if it needs updating
            try {
                $CurrentValue = Get-ItemPropertyValue -Path $RegKeyPath -Name $RegValName
            }
            catch {
                # If value does not exist, create it
                Set-ItemProperty -Path $RegKeyPath -Name $RegValName -Type $RegValType -Value $RegValData -Force
                Write-EnhancedLog -Message "Created registry value: $RegValName with data: $RegValData" -Level "INFO"
                return
            }

            if ($CurrentValue -ne $RegValData) {
                # If value exists but data is wrong, update the value
                Set-ItemProperty -Path $RegKeyPath -Name $RegValName -Type $RegValType -Value $RegValData -Force
                Write-EnhancedLog -Message "Updated registry value: $RegValName with data: $RegValData" -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "Registry value: $RegValName already has the correct data: $RegValData" -Level "INFO"
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Set-RegistryValue function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Set-RegistryValue function" -Level "Notice"
    }
}

# # Example call to the function
# $RegistrySettings = @(
# @{
# RegValName = "AllowTenantList"
# RegValType = "String"
# RegValData = "b5dae566-ad8f-44e1-9929-5669f1dbb343"
# }
# )

# foreach ($setting in $RegistrySettings) {
# # Define the parameters to be splatted
# $splatParams = @{
# RegKeyPath = "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive"
# RegValName = $setting.RegValName
# RegValType = $setting.RegValType
# RegValData = $setting.RegValData
# }

# # Call the function with splatted parameters
# Set-RegistryValue @splatParams
# }


# # Example usage
# $params = @{
# RegKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
# RegValName = "AutoAdminLogon"
# RegValType = "DWORD"
# RegValData = "0"
# }
# Set-RegistryValue @params
#EndRegion '.\Public\Set-RegistryValue.ps1' 206
#Region '.\Public\Set-RunOnce.ps1' -1

# function Set-RunOnce {
# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [string]$ScriptPath,
        
# [Parameter(Mandatory = $true)]
# [string]$RunOnceKey,
        
# [Parameter(Mandatory = $true)]
# [string]$PowershellPath,
        
# [Parameter(Mandatory = $true)]
# [string]$ExecutionPolicy,
        
# [Parameter(Mandatory = $true)]
# [string]$RunOnceName
# )

# Begin {
# Write-EnhancedLog -Message "Starting Set-RunOnce function" -Level "INFO"
# Log-Params -Params @{
# ScriptPath = $ScriptPath
# RunOnceKey = $RunOnceKey
# PowershellPath = $PowershellPath
# ExecutionPolicy = $ExecutionPolicy
# RunOnceName = $RunOnceName
# }
# }

# Process {
# try {
# Write-EnhancedLog -Message "Setting RunOnce script" -Level "INFO"
# $RunOnceValue = "$PowershellPath -executionPolicy $ExecutionPolicy -File $ScriptPath"
# Set-ItemProperty -Path $RunOnceKey -Name $RunOnceName -Value $RunOnceValue -Verbose
# } catch {
# Write-EnhancedLog -Message "An error occurred while setting RunOnce script: $($_.Exception.Message)" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Set-RunOnce function" -Level "INFO"
# }
# }

# # # Example usage with splatting
# # $SetRunOnceParams = @{
# # ScriptPath = "C:\YourScriptPath.ps1"
# # RunOnceKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
# # PowershellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe"
# # ExecutionPolicy = "Unrestricted"
# # RunOnceName = "NextRun"
# # }

# # Set-RunOnce @SetRunOnceParams







function Set-RunOnce {
    <#
    .SYNOPSIS
    Sets a RunOnce registry key to execute a specified script on the next system startup.
 
    .DESCRIPTION
    The Set-RunOnce function sets a RunOnce registry key to execute a specified PowerShell script on the next system startup. This can be useful for scheduling post-reboot tasks.
 
    .PARAMETER ScriptPath
    The path to the PowerShell script to be executed on the next system startup.
 
    .PARAMETER RunOnceKey
    The registry key path for the RunOnce entry.
 
    .PARAMETER PowershellPath
    The path to the PowerShell executable.
 
    .PARAMETER ExecutionPolicy
    The execution policy for running the PowerShell script.
 
    .PARAMETER RunOnceName
    The name of the RunOnce entry.
 
    .EXAMPLE
    $params = @{
        ScriptPath = "C:\ProgramData\AADMigration\Scripts\PostRunOnce2.ps1"
        RunOnceKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
        PowershellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe"
        ExecutionPolicy = "Unrestricted"
        RunOnceName = "NextRun"
    }
    Set-RunOnce @params
    Sets the RunOnce registry key to execute the specified script on the next system startup.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ScriptPath,
        
        [Parameter(Mandatory = $true)]
        [string]$RunOnceKey,
        
        [Parameter(Mandatory = $true)]
        [string]$PowershellPath,
        
        [Parameter(Mandatory = $true)]
        [string]$ExecutionPolicy,
        
        [Parameter(Mandatory = $true)]
        [string]$RunOnceName
    )

    Begin {
        Write-EnhancedLog -Message "Starting Set-RunOnce function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
        # Log-Params -Params @{
        # ScriptPath = $ScriptPath
        # RunOnceKey = $RunOnceKey
        # PowershellPath = $PowershellPath
        # ExecutionPolicy = $ExecutionPolicy
        # RunOnceName = $RunOnceName
        # }
    }

    Process {
        try {
            # Validate script path
            if (-not (Test-Path -Path $ScriptPath)) {
                Throw "Script file not found: $ScriptPath"
            }

            Write-EnhancedLog -Message "Setting RunOnce registry key for script: $ScriptPath" -Level "INFO"
            $RunOnceValue = "$PowershellPath -executionPolicy $ExecutionPolicy -File $ScriptPath"

            $params = @{
                Path  = $RunOnceKey
                Name  = $RunOnceName
                Value = $RunOnceValue
            }

            Set-ItemProperty @params
            Write-EnhancedLog -Message "RunOnce registry key set successfully." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Set-RunOnce function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Set-RunOnce function" -Level "Notice"
    }
}

# # Example usage with splatting
# $params = @{
# ScriptPath = "C:\ProgramData\AADMigration\Scripts\PostRunOnce2.ps1"
# RunOnceKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
# PowershellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe"
# ExecutionPolicy = "Unrestricted"
# RunOnceName = "NextRun"
# }
# Set-RunOnce @params
#EndRegion '.\Public\Set-RunOnce.ps1' 169
#Region '.\Public\Show-MigrationInProgressForm.ps1' -1

function Show-MigrationInProgressForm {
    <#
    .SYNOPSIS
    Displays a migration in progress form.
 
    .DESCRIPTION
    The Show-MigrationInProgressForm function displays a form with a "Migration in Progress" message and an image to indicate that a migration process is ongoing. The form is displayed in full-screen mode and prevents user interaction with other windows.
 
    .PARAMETER ImagePath
    The path to the image file to be displayed on the form.
 
    .EXAMPLE
    $params = @{
        ImagePath = "C:\ProgramData\AADMigration\Files\MigrationInProgress.bmp"
    }
    Show-MigrationInProgressForm @params
    Displays the migration in progress form with the specified image.
    #>


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

    Begin {
        Write-EnhancedLog -Message "Starting Show-MigrationInProgressForm function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            if (-not (Test-Path -Path $ImagePath)) {
                Throw "Image file not found: $ImagePath"
            }

            [void][reflection.assembly]::LoadWithPartialName("System.Drawing")
            [void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
            $img = [System.Drawing.Image]::FromFile($ImagePath)

            [System.Windows.Forms.Application]::EnableVisualStyles()
            $form = New-Object Windows.Forms.Form
            $form.Text = "Migration in Progress"
            $form.WindowState = 'Maximized'
            $form.BackColor = "#000000"
            $form.TopMost = $true

            $pictureBox = New-Object Windows.Forms.PictureBox
            $pictureBox.Width = $img.Size.Width
            $pictureBox.Height = $img.Size.Height
            $pictureBox.Dock = "Fill"
            $pictureBox.SizeMode = "StretchImage"
            $pictureBox.Image = $img
            $form.Controls.Add($pictureBox)
            $form.Add_Shown({ $form.Activate() })
            $form.Show()
            Write-EnhancedLog -Message "Displayed migration in progress form." -Level "INFO"

            # Keep the form open
            while ($form.Visible) {
                [System.Windows.Forms.Application]::DoEvents()
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Show-MigrationInProgressForm function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Show-MigrationInProgressForm function" -Level "Notice"
    }
}

# # Example usage
# $params = @{
# ImagePath = "C:\ProgramData\AADMigration\Files\MigrationInProgress.bmp"
# }
# Show-MigrationInProgressForm @params
#EndRegion '.\Public\Show-MigrationInProgressForm.ps1' 81
#Region '.\Public\Start-MigrationProcess.ps1' -1

function Start-MigrationProcess {
    <#
    .SYNOPSIS
    Starts the migration process by configuring settings, blocking user input, displaying a progress form, and installing a provisioning package.
 
    .DESCRIPTION
    The Start-MigrationProcess function configures migration settings, blocks user input, displays a migration progress form, sets a RunOnce script for post-reboot tasks, installs a provisioning package, and then restarts the computer.
 
    .PARAMETER MigrationConfigPath
    The path to the migration configuration file.
 
    .PARAMETER ImagePath
    The path to the image file to be displayed on the migration progress form.
 
    .PARAMETER RunOnceScriptPath
    The path to the PowerShell script to be executed on the next system startup.
 
    .PARAMETER RunOnceKey
    The registry key path for the RunOnce entry.
 
    .PARAMETER PowershellPath
    The path to the PowerShell executable.
 
    .PARAMETER ExecutionPolicy
    The execution policy for running the PowerShell script.
 
    .PARAMETER RunOnceName
    The name of the RunOnce entry.
 
    .EXAMPLE
    $params = @{
        MigrationConfigPath = "C:\ProgramData\AADMigration\scripts\MigrationConfig.psd1"
        ImagePath = "C:\ProgramData\AADMigration\Files\MigrationInProgress.bmp"
        RunOnceScriptPath = "C:\ProgramData\AADMigration\Scripts\PostRunOnce2.ps1"
        RunOnceKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
        PowershellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe"
        ExecutionPolicy = "Unrestricted"
        RunOnceName = "NextRun"
    }
    Start-MigrationProcess @params
    Starts the migration process.
    #>


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

        [Parameter(Mandatory = $true)]
        [string]$ImagePath,

        [Parameter(Mandatory = $true)]
        [string]$RunOnceScriptPath,

        [Parameter(Mandatory = $true)]
        [string]$RunOnceKey,

        [Parameter(Mandatory = $true)]
        [string]$PowershellPath,

        [Parameter(Mandatory = $true)]
        [string]$ExecutionPolicy,

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

    Begin {
        Write-EnhancedLog -Message "Starting Start-MigrationProcess function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            Start-Transcript -Path "C:\ProgramData\AADMigration\Logs\AD2AADJ-R1.txt" -NoClobber

            $MigrationConfig = Import-LocalizedData -BaseDirectory (Split-Path -Path $MigrationConfigPath) -FileName (Split-Path -Path $MigrationConfigPath -Leaf)
            $PPKGName = $MigrationConfig.ProvisioningPack
            $MigrationPath = $MigrationConfig.MigrationPath

            # Block user input
            $blockParams = @{
                Block = $true
            }
            Block-UserInput @blockParams

            # Show migration in progress form
            $formParams = @{
                ImagePath = $ImagePath
            }
            Show-MigrationInProgressForm @formParams

            # Set RunOnce script
            $runOnceParams = @{
                ScriptPath      = $RunOnceScriptPath
                RunOnceKey      = $RunOnceKey
                PowershellPath  = $PowershellPath
                ExecutionPolicy = $ExecutionPolicy
                RunOnceName     = $RunOnceName
            }
            Set-RunOnce @runOnceParams

            # Install provisioning package
            $installParams = @{
                PPKGName     = $PPKGName
                MigrationPath = $MigrationPath
            }
            Install-PPKG @installParams

            Stop-Transcript

            # Unblock user input and close form
            Block-UserInput -Block $false

            Restart-Computer
        }
        catch {
            Write-EnhancedLog -Message "An error occurred during the migration process: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Start-MigrationProcess function" -Level "Notice"
    }
}

# # Example usage
# $params = @{
# MigrationConfigPath = "C:\ProgramData\AADMigration\scripts\MigrationConfig.psd1"
# ImagePath = "C:\ProgramData\AADMigration\Files\MigrationInProgress.bmp"
# RunOnceScriptPath = "C:\ProgramData\AADMigration\Scripts\PostRunOnce2.ps1"
# RunOnceKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
# PowershellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe"
# ExecutionPolicy = "Unrestricted"
# RunOnceName = "NextRun"
# }
# Start-MigrationProcess @params
#EndRegion '.\Public\Start-MigrationProcess.ps1' 139
#Region '.\Public\Stop-ProcessesUsingOneDriveLib.ps1' -1

function Stop-ProcessesUsingOneDriveLib {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$OneDriveLibPath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Stop-ProcessesUsingOneDriveLib function" -Level "INFO"
        Log-Params -Params @{ OneDriveLibPath = $OneDriveLibPath }
    }

    Process {
        try {
            # Validate before removal
            $initialProcesses = Validate-OneDriveLibUsage -OneDriveLibPath $OneDriveLibPath
            if ($initialProcesses.Count -eq 0) {
                Write-EnhancedLog -Message "No processes found using OneDriveLib.dll before attempting termination." -Level "INFO"
            }

            # Terminate processes
            foreach ($process in $initialProcesses) {
                Write-EnhancedLog -Message "Found process using OneDriveLib.dll: $($process.ProcessName) (ID: $($process.ProcessId)). Attempting to terminate." -Level "WARNING"
                Stop-Process -Id $process.ProcessId -Force -ErrorAction Stop
            }

            # Validate after removal
            $remainingProcesses = Validate-OneDriveLibUsage -OneDriveLibPath $OneDriveLibPath
            if ($remainingProcesses.Count -eq 0) {
                Write-EnhancedLog -Message "Successfully terminated all processes using OneDriveLib.dll." -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "Some processes could not be terminated. Manual intervention may be required." -Level "ERROR"
                foreach ($process in $remainingProcesses) {
                    Write-EnhancedLog -Message "Process still running: $($process.ProcessName) (ID: $($process.ProcessId))." -Level "ERROR"
                }
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Stop-ProcessesUsingOneDriveLib function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Stop-ProcessesUsingOneDriveLib function" -Level "INFO"
    }
}

# Example usage
# Stop-ProcessesUsingOneDriveLib -OneDriveLibPath "C:\ProgramData\AADMigration\Files\OneDriveLib.dll"
#EndRegion '.\Public\Stop-ProcessesUsingOneDriveLib.ps1' 52
#Region '.\Public\Suspend-BitLockerWithReboot.ps1' -1

function Suspend-BitLockerWithReboot {
    <#
    .SYNOPSIS
    Suspends BitLocker and configures the system to reboot a specified number of times.
 
    .DESCRIPTION
    The Suspend-BitLockerWithReboot function suspends BitLocker protection on the specified drive and configures the system to reboot a specified number of times.
 
    .PARAMETER MountPoint
    The drive letter of the BitLocker protected drive.
 
    .PARAMETER RebootCount
    The number of reboots to suspend BitLocker protection for.
 
    .EXAMPLE
    $params = @{
        MountPoint = "C:"
        RebootCount = 2
    }
    Suspend-BitLockerWithReboot @params
    Suspends BitLocker on drive C: for 2 reboots.
    #>


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

        [Parameter(Mandatory = $true)]
        [int]$RebootCount
    )

    Begin {
        Write-EnhancedLog -Message "Starting Suspend-BitLockerWithReboot function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            Write-EnhancedLog -Message "Suspending BitLocker on drive $MountPoint for $RebootCount reboots" -Level "INFO"
            Suspend-BitLocker -MountPoint $MountPoint -RebootCount $RebootCount -Verbose
            Write-EnhancedLog -Message "Successfully suspended BitLocker on drive $MountPoint" -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while suspending BitLocker: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Suspend-BitLockerWithReboot function" -Level "Notice"
    }
}

# # Example usage
# $params = @{
# MountPoint = "C:"
# RebootCount = 2
# }
# Suspend-BitLockerWithReboot @params
#EndRegion '.\Public\Suspend-BitLockerWithReboot.ps1' 62
#Region '.\Public\Test-ProvisioningPack.ps1' -1

function Test-ProvisioningPack {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$PPKGName
    )

    Begin {
        Write-EnhancedLog -Message "Starting Test-ProvisioningPack function" -Level "INFO"
        Log-Params -Params @{'PPKGName' = $PPKGName}
    }

    Process {
        try {
            Write-EnhancedLog -Message "Testing to see if provisioning package previously installed" -Level "INFO"
            $PPKGStatus = Get-ProvisioningPackage | Where-Object { $_.PackagePath -like "*$PPKGName*" }
            if ($PPKGStatus) {
                Write-EnhancedLog -Message "Provisioning package previously installed. Removing PPKG." -Level "INFO"
                $PPKGID = $PPKGStatus.PackageID
                Remove-ProvisioningPackage -PackageId $PPKGID
            }
        } catch {
            Write-EnhancedLog -Message "An error occurred while testing provisioning pack: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Test-ProvisioningPack function" -Level "INFO"
    }
}

# # Example usage with splatting
# $TestProvisioningPackParams = @{
# PPKGName = "YourProvisioningPackName"
# }

# Test-ProvisioningPack @TestProvisioningPackParams
#EndRegion '.\Public\Test-ProvisioningPack.ps1' 39
#Region '.\Public\Validate-OneDriveLibUsage.ps1' -1

function Validate-OneDriveLibUsage {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$OneDriveLibPath
    )

    $processesUsingLib = [System.Collections.Generic.List[PSCustomObject]]::new()

    try {
        # Get all processes
        $processes = Get-Process

        # Iterate over each process and check if it has loaded OneDriveLib.dll
        foreach ($process in $processes) {
            try {
                $modules = $process.Modules | Where-Object { $_.FileName -eq $OneDriveLibPath }
                if ($modules) {
                    $processesUsingLib.Add([PSCustomObject]@{
                        ProcessName = $process.ProcessName
                        ProcessId   = $process.Id
                    })
                }
            }
            catch {
                # Handle any errors encountered while accessing process modules
                Write-EnhancedLog -Message "Could not access modules for process: $($process.ProcessName) (ID: $($process.Id)). Error: $($_.Exception.Message)" -Level "WARNING"
            }
        }
    }
    catch {
        Write-EnhancedLog -Message "An error occurred in Validate-OneDriveLibUsage function: $($_.Exception.Message)" -Level "ERROR"
        Handle-Error -ErrorRecord $_
    }

    return $processesUsingLib
}
#EndRegion '.\Public\Validate-OneDriveLibUsage.ps1' 38