EnhancedWin32DeployerAO.psm1

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

function Add-GuidToPs1Files {


    <#
.SYNOPSIS
Adds a unique GUID and timestamp to the top of each .ps1 file in a specified directory.
 
.DESCRIPTION
This function searches for PowerShell script files (.ps1) within a specified subdirectory of a given root directory. It then prepends a unique GUID and a timestamp to each file for tracking purposes. This is useful for marking scripts in bulk operations or deployments.
 
.PARAMETER AOscriptDirectory
The root directory under which the target program folder resides.
 
.PARAMETER programfoldername
The name of the subdirectory containing the .ps1 files to be modified.
 
.EXAMPLE
Add-GuidToPs1Files -AOscriptDirectory "d:\Scripts" -programfoldername "MyProgram"
 
Adds a tracking GUID and timestamp to all .ps1 files under "d:\Scripts\apps-winget\MyProgram".
 
.NOTES
Author: Your Name
Date: Get the current date
Version: 1.0
 
#>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        # [ValidateScript({Test-Path $_})]
        [string]$AOscriptDirectory,

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

    # Helper function for logging
    Begin {
        Write-EnhancedLog -Message "Starting to modify PowerShell files." -Level "INFO" -ForegroundColor Green
    }

    Process {
        $targetFolder = Join-Path -Path $AOscriptDirectory -ChildPath "apps-winget\$programfoldername"

        if (-Not (Test-Path -Path $targetFolder)) {
            Write-EnhancedLog -Message "The target folder does not exist: $targetFolder" -Level "ERROR" -ForegroundColor Red
            return
        }

        $ps1Files = Get-ChildItem -Path $targetFolder -Filter *.ps1 -Recurse
        if ($ps1Files.Count -eq 0) {
            Write-EnhancedLog -Message "No PowerShell files (.ps1) found in $targetFolder" -Level "WARNING" -ForegroundColor Yellow
            return
        }

        foreach ($file in $ps1Files) {
            try {
                $content = Get-Content -Path $file.FullName -ErrorAction Stop
                $pattern = '^#Unique Tracking ID: .+'
                $content = $content | Where-Object { $_ -notmatch $pattern }

                $guid = [guid]::NewGuid().ToString("D").ToLower()
                $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
                $lineToAdd = "#Unique Tracking ID: $guid, Timestamp: $timestamp"
                $newContent = $lineToAdd, $content

                Set-Content -Path $file.FullName -Value $newContent -ErrorAction Stop
                Write-EnhancedLog -Message "Modified file: $($file.FullName)" -Level "VERBOSE" -ForegroundColor Yellow
            }
            catch {
                Write-EnhancedLog -Message "Failed to modify file: $($file.FullName). Error: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red
            }
        }
    }

    End {
        Write-EnhancedLog -Message "Completed modifications." -Level "INFO" -ForegroundColor Cyan
    }
}


# Example usage:
# Add-GuidToPs1Files -AOscriptDirectory $AOscriptDirectory
#EndRegion '.\Public\Add-GuidToPs1Files.ps1' 86
#Region '.\Public\Check-ApplicationImage.ps1' -1

function Check-ApplicationImage {
    <#
    .SYNOPSIS
    Checks for the presence of any PNG image in the program folder.
 
    .DESCRIPTION
    This function looks for any PNG image in the folder where the program resides. If found, it assigns the image path to the variable `$Prg_img`. If no image is found, a default template image is assigned. The function returns an object containing the image path.
 
    .PARAMETER Prg
    The program object containing metadata like the program ID.
 
    .PARAMETER Prg_Path
    The path to the program folder where the PNG image is searched.
 
    .EXAMPLE
    $imageDetails = Check-ApplicationImage -Prg $Prg -Prg_Path "C:\Repo\MyApp"
    Checks for any PNG image in the folder and returns the image details.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the program object.")]
        [ValidateNotNullOrEmpty()]
        [PSCustomObject]$Prg,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the program folder.")]
        [ValidateNotNullOrEmpty()]
        [string]$Prg_Path
    )

    Begin {
        Write-EnhancedLog -Message "Starting Check-ApplicationImage function for program: $($Prg.id)" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Search for any .png file in the program folder
            $imageFiles = Get-ChildItem -Path $Prg_Path -Filter "*.png" -ErrorAction Stop

            if ($imageFiles.Count -gt 0) {
                # Use the first PNG found in the folder
                $Prg_img = $imageFiles[0].FullName
                Write-EnhancedLog -Message "Application image found: $Prg_img" -Level "INFO"
            }
            else {
                # Assign default image if no PNG found
                $Prg_img = Join-Path -Path $Repo_Path -ChildPath "resources\template\winget\winget-managed.png"
                Write-EnhancedLog -Message "No PNG found. Using default image: $Prg_img" -Level "INFO"
            }

            # Return the image path details as an object
            $imageDetails = [pscustomobject]@{
                ProgramID  = $Prg.id
                ImagePath  = $Prg_img
                ImageFound = ($imageFiles.Count -gt 0)
            }

            return $imageDetails
        }
        catch {
            Write-EnhancedLog -Message "Error occurred during image check: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
    }

    End {
        Write-EnhancedLog -Message "Completed Check-ApplicationImage function for program: $($Prg.id)" -Level "INFO"
    }
}



# # Run Check-ApplicationImage and store the returned object
# $imageDetails = Check-ApplicationImage -Prg $Prg

# # Access the properties of the returned object
# Write-Host "Program ID: $($imageDetails.ProgramID)"
# Write-Host "Image Path: $($imageDetails.ImagePath)"
# Write-Host "Image Found: $($imageDetails.ImageFound)"

#EndRegion '.\Public\Check-ApplicationImage.ps1' 83
#Region '.\Public\Compile-Win32_intunewin.ps1' -1

function Compile-Win32_intunewin {
    <#
    .SYNOPSIS
    Compiles a Win32 app into an Intune Win format for deployment.
 
    .DESCRIPTION
    This function compiles a Win32 application into an Intune Win32 format. It checks for an existing application image, downloads the latest IntuneWinAppUtil if necessary, and uploads the compiled application.
 
    .PARAMETER Prg
    The application object with details needed for compilation.
 
    .PARAMETER Repo_winget
    The repository path for winget.
 
    .PARAMETER Repo_Path
    The repository path for storing resources.
 
    .PARAMETER Prg_Path
    The path to the program being compiled.
 
    .EXAMPLE
    $params = @{
        Prg = [pscustomobject]@{ id = 'exampleApp'; name = 'Example' }
        Repo_winget = "https://example.com/winget"
        Repo_Path = "C:\Repos"
        Prg_Path = "C:\Programs\ExampleApp"
    }
    Compile-Win32_intunewin @params
    Compiles the Win32 app and uploads it to Intune.
    #>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, HelpMessage = "Provide the application object containing necessary details.")]
        [ValidateNotNullOrEmpty()]
        [pscustomobject]$Prg,
    
        [Parameter(Mandatory = $true, HelpMessage = "Specify the Winget repository URL.")]
        [ValidateNotNullOrEmpty()]
        [string]$Repo_winget,
    
        [Parameter(Mandatory = $true, HelpMessage = "Specify the path to the repository for storing resources.")]
        [ValidateNotNullOrEmpty()]
        [string]$Repo_Path,
    
        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the program.")]
        [ValidateNotNullOrEmpty()]
        [string]$Prg_Path
    )
    
    

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

        # Check for the program image
        Write-EnhancedLog -Message "Checking for program image for $($Prg.id)" -Level "INFO"
        $Prg_img = if (Test-Path -Path (Join-Path -Path $Prg_Path -ChildPath "$($Prg.id).png")) {
            Join-Path -Path $Prg_Path -ChildPath "$($Prg.id).png"
        }
        else {
            "$Repo_Path\resources\template\winget\winget-managed.png"
        }
    }

    Process {
        if ($PSCmdlet.ShouldProcess("Win32 App Compilation for $($Prg.id)", "Uploading to Intune")) {
            try {
                Write-EnhancedLog -Message "Processing Win32 app: $($Prg.id)" -Level "INFO"
                # Log the program path and image path
                Write-EnhancedLog -Message "Program path: $Prg_Path" -Level "INFO"
                Write-EnhancedLog -Message "Program image: $Prg_img" -Level "INFO"
    
                # Upload the Win32 app
                Write-EnhancedLog -Message "Uploading Win32 app to Intune" -Level "INFO"
                # Upload-Win32App -Prg $Prg -Prg_Path $Prg_Path -Prg_img $Prg_img

                # Calling Upload-Win32App inside PowerShell 5
                Invoke-CommandInPS5 -Command "Upload-Win32App -Prg $Prg -Prg_Path $Prg_Path -Prg_img $Prg_img"
            }
            catch {
                Write-EnhancedLog -Message "Error during Win32 app processing: $($_.Exception.Message)" -Level "ERROR"
                Handle-Error -ErrorRecord $_
                throw
            }
        }
        else {
            Write-EnhancedLog -Message "Operation skipped due to WhatIf or confirmation." -Level "INFO"
        }
    }
    

    End {
        Write-EnhancedLog -Message "Exiting Compile-Win32_intunewin function" -Level "Notice"
    }
}
#EndRegion '.\Public\Compile-Win32_intunewin.ps1' 98
#Region '.\Public\Copy-InvokeAzureStorageBlobUploadFinalize.ps1' -1

function Copy-InvokeAzureStorageBlobUploadFinalize {
    <#
    .SYNOPSIS
    Copies the AzureStorageBlobUploadFinalize script to specified destination paths.
 
    .DESCRIPTION
    This function copies the `Invoke-AzureStorageBlobUploadFinalize.ps1` script from the source path to multiple destination paths. It logs the success or failure of each copy operation and returns an object containing the result of each file copy. At the end, it provides a summary report with color-coded statuses.
 
    .PARAMETER sourceFile
    The path to the source file that needs to be copied.
 
    .PARAMETER destinationPaths
    An array of destination paths where the source file will be copied.
 
    .EXAMPLE
    $copyResults = Copy-InvokeAzureStorageBlobUploadFinalize -sourceFile "C:\Code\IntuneWin32App\Private\Invoke-AzureStorageBlobUploadFinalize.ps1" -destinationPaths @("C:\Path1", "C:\Path2")
    Processes the file copy and returns the results with a summary report.
    #>


    [CmdletBinding()]
    param (
        [string]$sourceFile = "C:\Code\IntuneWin32App\Private\Invoke-AzureStorageBlobUploadFinalize.ps1",
        [string[]]$destinationPaths = @(
            "C:\Program Files\WindowsPowerShell\Modules\IntuneWin32App\1.4.4\Private\Invoke-AzureStorageBlobUploadFinalize.ps1"
        )
    )

    Begin {
        Write-EnhancedLog -Message "Starting the file copy process..." -Level "INFO"

        # Initialize counters and status list
        $totalCopies = 0
        $successfulCopies = 0
        $failedCopies = 0
        $copyStatuses = [System.Collections.Generic.List[PSCustomObject]]::new()  # Efficient list initialization
    }

    Process {
        foreach ($destination in $destinationPaths) {
            try {
                Write-EnhancedLog -Message "Copying file to $destination" -Level "INFO"
                $totalCopies++

                # Splatting Copy-Item parameters
                $CopyItemParams = @{
                    Path        = $sourceFile
                    Destination = $destination
                    Force       = $true
                }

                Copy-Item @CopyItemParams
                Write-EnhancedLog -Message "Successfully copied to $destination" -Level "INFO"
                $successfulCopies++
                $copyStatuses.Add([pscustomobject]@{ Destination = $destination; Status = "Success" })
            }
            catch {
                Write-EnhancedLog -Message "Failed to copy to $destination. Error: $_" -Level "ERROR"
                Handle-Error -ErrorRecord $_
                $failedCopies++
                $copyStatuses.Add([pscustomobject]@{ Destination = $destination; Status = "Failed" })
            }
        }
    }

    End {
        Write-EnhancedLog -Message "File copy process completed." -Level "INFO"

        # Return object with summary of copy operations
        $copyResults = [pscustomobject]@{
            TotalCopies       = $totalCopies
            SuccessfulCopies  = $successfulCopies
            FailedCopies      = $failedCopies
            CopyStatuses      = $copyStatuses
        }

        # Print final summary report
        Write-Host "Final Summary Report" -ForegroundColor Green
        Write-Host "---------------------" -ForegroundColor Green
        Write-Host "Total Copies: $totalCopies" -ForegroundColor Green
        Write-Host "Successful Copies: $successfulCopies" -ForegroundColor Green
        Write-Host "Failed Copies: $failedCopies" -ForegroundColor Red

        # Loop through copyStatuses for detailed report
        foreach ($copyStatus in $copyStatuses) {
            if ($copyStatus.Status -eq "Success") {
                Write-Host "Destination: $($copyStatus.Destination) - Status: $($copyStatus.Status)" -ForegroundColor Green
            }
            else {
                Write-Host "Destination: $($copyStatus.Destination) - Status: $($copyStatus.Status)" -ForegroundColor Red
            }
        }

        return $copyResults
    }
}

# Example Usage:
# $copyResults = Copy-InvokeAzureStorageBlobUploadFinalize -sourceFile "C:\Code\IntuneWin32App\Private\Invoke-AzureStorageBlobUploadFinalize.ps1" -destinationPaths @("C:\Path1", "C:\Path2")
# Write-Host "Total Copies: $($copyResults.TotalCopies)"
#EndRegion '.\Public\Copy-InvokeAzureStorageBlobUploadFinalize.ps1' 100
#Region '.\Public\Create-AADGroup.ps1' -1

function Create-AADGroup ($Prg) {


    # # Convert the Client Secret to a SecureString
    # $SecureClientSecret = ConvertTo-SecureString $connectionParams.ClientSecret -AsPlainText -Force

    # # Create a PSCredential object with the Client ID as the user and the Client Secret as the password
    # $ClientSecretCredential = New-Object System.Management.Automation.PSCredential ($connectionParams.ClientId, $SecureClientSecret)

    # # Connect to Microsoft Graph
    # Connect-MgGraph -TenantId $connectionParams.TenantId -ClientSecretCredential $ClientSecretCredential

    # Your code that interacts with Microsoft Graph goes here


    # Create Group
    # $grpname = "$($global:SettingsVAR.AADgrpPrefix )$($Prg.id)"
    Write-EnhancedLog -Message "setting Group Name" -Level "WARNING" -ForegroundColor ([ConsoleColor]::Yellow)
    $grpname = "SG007 - Intune - Apps - Microsoft Teams - WinGet - Windows Package Manager"
    if (!$(Get-MgGroup -Filter "DisplayName eq '$grpname'")) {
        # Write-Host " Create AAD group for assigment: $grpname" -Foregroundcolor cyan

        Write-EnhancedLog -Message " Did not find Group $grpname " -Level "WARNING" -ForegroundColor ([ConsoleColor]::Yellow)
        
        # $GrpObj = New-MgGroup -DisplayName "$grpname" -Description "App assigment: $($Prg.id) $($Prg.manager)" -MailEnabled:$False -MailNickName $grpname -SecurityEnabled
    }
    else { $GrpObj = Get-MgGroup -Filter "DisplayName eq '$grpname'" }


    Write-EnhancedLog -Message " Assign Group > $grpname < to > $($Prg.Name)" -Level "WARNING" -ForegroundColor ([ConsoleColor]::Yellow)
  


    Write-EnhancedLog -Message " calling Get-IntuneWin32App " -Level "WARNING" -ForegroundColor ([ConsoleColor]::Yellow)
    $Win32App = Get-IntuneWin32App -DisplayName "$($Prg.Name)"


    Write-EnhancedLog -Message " calling Get-IntuneWin32App - done " -Level "INFO" -ForegroundColor ([ConsoleColor]::Green)


    Write-EnhancedLog -Message " calling Add-IntuneWin32AppAssignmentGroup " -Level "WARNING" -ForegroundColor ([ConsoleColor]::Yellow)
    Add-IntuneWin32AppAssignmentGroup -Include -ID $Win32App.id -GroupID $GrpObj.id -Intent "available" -Notification "showAll"


    Write-EnhancedLog -Message " calling Add-IntuneWin32AppAssignmentGroup - done " -Level "INFO" -ForegroundColor ([ConsoleColor]::Green)
}
#EndRegion '.\Public\Create-AADGroup.ps1' 47
#Region '.\Public\Create-DetectionRule.ps1' -1

function Create-DetectionRule {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Prg_Path
    )

    Write-EnhancedLog -Message "Creating detection rule..." -Level "WARNING"
    $detectionScriptPath = Join-Path -Path $Prg_Path -ChildPath "check.ps1"
    if (-not (Test-Path -Path $detectionScriptPath)) {
        Write-Warning "Detection rule script file does not exist at path: $detectionScriptPath"
    }
    else {
        $DetectionRule = New-IntuneWin32AppDetectionRuleScript -ScriptFile $detectionScriptPath -EnforceSignatureCheck $false -RunAs32Bit $false
    }
    Write-EnhancedLog -Message "Detection rule set (calling New-IntuneWin32AppDetectionRuleScript) - done" -Level "INFO"

    return $DetectionRule
}
#EndRegion '.\Public\Create-DetectionRule.ps1' 19
#Region '.\Public\Create-IntuneWinPackage.ps1' -1

function Create-IntuneWinPackage {
    <#
    .SYNOPSIS
    Creates a .intunewin package for a specified program.
 
    .DESCRIPTION
    This function creates a .intunewin package by packaging the source folder and setup file. It logs the process and handles errors. The resulting package path is returned.
 
    .PARAMETER Prg
    The program object containing metadata like the program name.
 
    .PARAMETER Prg_Path
    The path to the program source folder.
 
    .PARAMETER destinationPath
    The destination path where the .intunewin package will be created.
 
    .EXAMPLE
    $IntuneWinFile = Create-IntuneWinPackage -Prg $Prg -Prg_Path "C:\Path\To\Program" -destinationPath "C:\Path\To\Destination"
    Creates the .intunewin package and returns the file path.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the program object.")]
        [ValidateNotNullOrEmpty()]
        [pscustomobject]$Prg,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the program source path.")]
        [ValidateNotNullOrEmpty()]
        [string]$Prg_Path,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the destination path for the package.")]
        [ValidateNotNullOrEmpty()]
        [string]$destinationPath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Create-IntuneWinPackage function for program: $($Prg.Name)" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            Write-EnhancedLog -Message "Creating .intunewin package..." -Level "INFO"

            $setupFile = "install.ps1"



            #Option 1: using SvRooij.ContentPrep.Cmdlet Module

            # Use New-IntuneWinPackage to create the package
            # New-IntuneWinPackage -SourcePath $Prg_Path -DestinationPath $destinationPath -SetupFile $setupFile -Verbose



            #Option 2: using IntuneWin32App Module

            # Splatting for New-IntuneWin32AppPackage
            $NewIntuneWinPackageParams = @{
                SourceFolder = $Prg_Path
                SetupFile    = $setupFile
                OutputFolder = $destinationPath
                Verbose      = $true
                Force        = $true
            }

            # Use New-IntuneWin32AppPackage to create the package
            $Win32AppPackage = New-IntuneWin32AppPackage @NewIntuneWinPackageParams

            Write-EnhancedLog -Message "Package creation completed successfully." -Level "INFO"

            # Set the IntuneWin file path
            $IntuneWinFile = Join-Path -Path $destinationPath -ChildPath "install.intunewin"
            Write-EnhancedLog -Message "IntuneWinFile path set: $IntuneWinFile" -Level "INFO"

            # Return the path of the created package
            return $IntuneWinFile
        }
        catch {
            Write-EnhancedLog -Message "Error creating .intunewin package: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            Write-Host "Error creating .intunewin package: $($_.Exception.Message)" -ForegroundColor Red
            throw
        }
    }

    End {
        Write-EnhancedLog -Message "Completed Create-IntuneWinPackage function for program: $($Prg.Name)" -Level "INFO"

        # Print summary report
        Write-Host "Summary Report" -ForegroundColor Green
        Write-Host "-----------------" -ForegroundColor Green
        Write-Host "Program Name: $($Prg.Name)" -ForegroundColor Green
        Write-Host "Source Path: $Prg_Path" -ForegroundColor Green
        Write-Host "Destination Path: $destinationPath" -ForegroundColor Green
        Write-Host "IntuneWinFile: $IntuneWinFile" -ForegroundColor Green
    }
}
#EndRegion '.\Public\Create-IntuneWinPackage.ps1' 101
#Region '.\Public\Create-RequirementRule.ps1' -1

function Create-RequirementRule {
    Write-EnhancedLog -Message "Setting minimum requirements..." -Level "WARNING"
    $RequirementRule = New-IntuneWin32AppRequirementRule -Architecture "x64" -MinimumSupportedWindowsRelease "W10_1607"
    Write-EnhancedLog -Message "Minimum requirements set - done" -Level "INFO"

    return $RequirementRule
}
#EndRegion '.\Public\Create-RequirementRule.ps1' 8
#Region '.\Public\Define-SourcePath.ps1' -1

function Define-SourcePath {
    <#
    .SYNOPSIS
    Defines the source path for a given program.
 
    .DESCRIPTION
    This function dynamically constructs the source path for a program using the provided `Repo_winget` path and the program's ID. It logs the path and returns the path as part of an object.
 
    .PARAMETER Prg
    The program object containing metadata like the ID of the program.
 
    .PARAMETER Repo_winget
    The path to the repository (winget) where the program source resides.
 
    .EXAMPLE
    $sourcePathDetails = Define-SourcePath -Prg $Prg -Repo_winget "C:\Repo\winget"
    Defines the source path for the program and returns the path details.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the program object.")]
        [ValidateNotNullOrEmpty()]
        [PSCustomObject]$Prg,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the winget repository.")]
        [ValidateNotNullOrEmpty()]
        [string]$Repo_winget
    )

    Begin {
        Write-EnhancedLog -Message "Starting Define-SourcePath function for program: $($Prg.id)" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Construct the source path using the program ID and the Repo_winget path
            $sourcePath = Join-Path -Path $Repo_winget -ChildPath $Prg.id
            $Prg_Path = $sourcePath

            # Log the source path
            Write-EnhancedLog -Message "Source path defined: $Prg_Path" -Level "INFO"

            # Return the source path as part of an object
            return [pscustomobject]@{
                ProgramID   = $Prg.id
                SourcePath  = $sourcePath
            }
        }
        catch {
            Write-EnhancedLog -Message "Error occurred during Define-SourcePath: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
    }

    End {
        Write-EnhancedLog -Message "Completed Define-SourcePath function." -Level "INFO"
    }
}




# # Run Define-SourcePath and store the returned object
# Example usage of the Define-SourcePath function
# $sourcePathDetails = Define-SourcePath -Prg $Prg -Repo_winget "C:\Repo\winget"

# # Access the returned object properties
# Write-Host "Program ID: $($sourcePathDetails.ProgramID)"
# Write-Host "Source Path: $($sourcePathDetails.SourcePath)"


# Program ID: MyProgram
# Source Path: C:\path\to\Win32Apps-DropBox\MyProgram
#EndRegion '.\Public\Define-SourcePath.ps1' 77
#Region '.\Public\Get-CustomWin32AppName.ps1' -1


function Get-CustomWin32AppName {
    [CmdletBinding()]
    param(
        [string]$PRGID
    )
    process {
        if (-not [string]::IsNullOrWhiteSpace($PRGID)) {
            return $PRGID  # Directly return PRGID if it's valid
        }
        else {
            return "DefaultAppName"  # Fallback if PRGID is not provided
        }
    }
}


# Get-CustomWin32AppName
#EndRegion '.\Public\Get-CustomWin32AppName.ps1' 19
#Region '.\Public\Initialize-Win32Environment.ps1' -1

function Initialize-Win32Environment {
    <#
    .SYNOPSIS
    Initializes the Win32 environment by setting up platform-specific configurations.
 
    .DESCRIPTION
    This function detects the platform (Windows or Unix) and sets up the environment accordingly. It throws an error if the operating system is unsupported, with proper error handling. The function returns an object containing platform details and the environment setup results.
 
    .PARAMETER scriptpath
    The path to the script being executed, used for logging or module setup purposes.
 
    .EXAMPLE
    $envInitialization = Initialize-Win32Environment -scriptpath "C:\path\to\your\script.ps1"
    Initializes the Win32 environment for the specified script path and returns initialization details.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the script being executed.")]
        [ValidateNotNullOrEmpty()]
        [string]$scriptpath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Initialize-Win32Environment function" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            $platform = Get-Platform
            $envDetails = $null

            if ($platform -eq 'Win32NT' -or $platform -eq [System.PlatformID]::Win32NT) {
                Write-EnhancedLog -Message "Detected platform: Windows (Win32NT)" -Level "INFO"
                $envDetails = Setup-WindowsEnvironment -scriptpath $scriptpath
            }
            elseif ($platform -eq 'Unix' -or $platform -eq [System.PlatformID]::Unix) {
                Write-EnhancedLog -Message "Detected platform: Unix" -Level "INFO"
                # Setup-LinuxEnvironment (commented out for now)
                $envDetails = [pscustomobject]@{ Platform = "Unix"; Setup = "Not implemented" }
            }
            else {
                Write-EnhancedLog -Message "Unsupported operating system detected: $platform" -Level "ERROR"
                throw "Unsupported operating system: $platform"
            }

            # Return the platform and environment setup details as an object
            return [pscustomobject]@{
                Platform   = $platform
                EnvDetails = $envDetails
            }
        }
        catch {
            Write-EnhancedLog -Message "Error occurred during environment initialization: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
    }

    End {
        Write-EnhancedLog -Message "Environment initialization completed for script: $scriptpath" -Level "INFO"
    }
}


# Run Initialize-Win32Environment and store the returned object
# $envInitialization = Initialize-Win32Environment -scriptpath "C:\path\to\your\script.ps1"

# Access the properties of the returned object
# Write-Host "Platform: $($envInitialization.Platform)"
# Write-Host "Environment Details: $($envInitialization.EnvDetails)"


# Platform: Win32NT
# Environment Details: @{AOscriptDirectory=C:\path\to\Win32Apps-DropBox; directoryPath=C:\path\to\Win32Apps-DropBox; Repo_Path=C:\path\to; Repo_winget=C:\path\to\Win32Apps-DropBox}



# # Run Initialize-Win32Environment and store the returned object
# $envInitialization = Initialize-Win32Environment -scriptpath "C:\path\to\your\script.ps1"

# # Access the properties of the EnvDetails object
# $AOscriptDirectory = $envInitialization.EnvDetails.AOscriptDirectory
# $directoryPath = $envInitialization.EnvDetails.directoryPath
# $Repo_Path = $envInitialization.EnvDetails.Repo_Path
# $Repo_winget = $envInitialization.EnvDetails.Repo_winget

# # Output the extracted values
# Write-Host "AO Script Directory: $AOscriptDirectory"
# Write-Host "Directory Path: $directoryPath"
# Write-Host "Repository Path: $Repo_Path"
# Write-Host "Winget Path: $Repo_winget"
#EndRegion '.\Public\Initialize-Win32Environment.ps1' 94
#Region '.\Public\Invoke-PrinterInstallation.ps1' -1

function Invoke-PrinterInstallation {

    <#
.SYNOPSIS
Installs or uninstalls printer drivers based on JSON configuration files.
 
.DESCRIPTION
This PowerShell function reads printer installation settings from a specified printer configuration JSON file (printer.json) and application configuration JSON file (config.json). It constructs and optionally executes command lines for installing or uninstalling printer drivers. The function proceeds only if the 'PrinterInstall' attribute in the application configuration is set to true.
 
.PARAMETER PrinterConfigPath
The full file path to the printer configuration JSON file (printer.json). This file contains the printer settings such as PrinterName, PrinterIPAddress, PortName, DriverName, InfPathRelative, InfFileName, and DriverIdentifier.
 
.PARAMETER AppConfigPath
The full file path to the application configuration JSON file (config.json). This file contains application-wide settings including the 'PrinterInstall' flag that controls whether the installation or uninstallation should proceed.
 
.EXAMPLE
.\Invoke-PrinterInstallation -PrinterConfigPath "d:\path\to\printer.json" -AppConfigPath "d:\path\to\config.json"
 
Executes the Invoke-PrinterInstallation function using the specified printer and application configuration files. It constructs and displays the install and uninstall commands based on the configurations.
 
.INPUTS
None. You cannot pipe objects to Invoke-PrinterInstallation.
 
.OUTPUTS
String. Outputs the constructed install and uninstall commands to the console.
 
.NOTES
Version: 1.0
Author: Your Name
Creation Date: The Date
Purpose/Change: Initial function development
 
.LINK
URL to more information if available
 
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$PrinterConfigPath, # Path to printer.json

        [Parameter(Mandatory = $true)]
        [string]$AppConfigPath  # Path to config.json
    )

    Begin {
        Write-EnhancedLog -Message "Starting Invoke-PrinterInstallation" -Level "INFO" -ForegroundColor Green
    }

    Process {
        try {
            if (-not (Test-Path -Path $PrinterConfigPath)) {
                Write-EnhancedLog -Message "Printer configuration file not found at path: $PrinterConfigPath" -Level "ERROR" -ForegroundColor Red
                throw "Printer configuration file not found."
            }

            if (-not (Test-Path -Path $AppConfigPath)) {
                Write-EnhancedLog -Message "Application configuration file not found at path: $AppConfigPath" -Level "ERROR" -ForegroundColor Red
                throw "Application configuration file not found."
            }

            $appConfig = Get-Content -Path $AppConfigPath -Raw | ConvertFrom-Json

            if ($appConfig.PrinterInstall -eq $true) {
                $printerConfig = Get-Content -Path $PrinterConfigPath -Raw | ConvertFrom-Json

                $InstallCommandLine = "%SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -windowstyle hidden -executionpolicy bypass -File ""install.ps1"""
                $UninstallCommandLine = "%SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -windowstyle hidden -executionpolicy bypass -File ""uninstall.ps1"""

                $printerConfig.psobject.Properties | ForEach-Object {
                    $InstallCommandLine += " -$($_.Name) `"$($_.Value)`""
                    $UninstallCommandLine += " -$($_.Name) `"$($_.Value)`""
                }

                Write-EnhancedLog -Message "Install and Uninstall command lines constructed successfully" -Level "VERBOSE" -ForegroundColor Cyan

                # Return a custom object containing both commands
                $commands = [PSCustomObject]@{
                    InstallCommand   = $InstallCommandLine
                    UninstallCommand = $UninstallCommandLine
                }

                return $commands

            }
            else {
                Write-EnhancedLog -Message "PrinterInstall is not set to true in the application configuration. No commands will be executed." -Level "WARNING" -ForegroundColor Yellow
            }

        }
        catch {
            Write-EnhancedLog -Message "An error occurred: $_" -Level "ERROR" -ForegroundColor Red
        }
    }

    End {
        Write-EnhancedLog -Message "Invoke-PrinterInstallation completed" -Level "INFO" -ForegroundColor Green
    }
}


# # Define paths to the configuration files
# $printerConfigPath = Join-Path -Path $PSScriptRoot -ChildPath "printer.json"
# $appConfigPath = Join-Path -Path $PSScriptRoot -ChildPath "config.json"

# Invoke-PrinterInstallation -PrinterConfigPath $printerConfigPath -AppConfigPath $appConfigPath
#EndRegion '.\Public\Invoke-PrinterInstallation.ps1' 108
#Region '.\Public\Prepare-Paths.ps1' -1

function Prepare-Paths {
    <#
    .SYNOPSIS
    Prepares the necessary paths for Win32 app deployment.
 
    .DESCRIPTION
    This function checks for and creates the required directories for a given program. It returns the destination path as part of an object.
 
    .PARAMETER Prg
    The program object containing metadata like the name of the program.
 
    .PARAMETER Prg_Path
    The source path where the program files are located.
 
    .PARAMETER Win32AppsRootPath
    The root path for storing the Win32 apps.
 
    .EXAMPLE
    $paths = Prepare-Paths -Prg $Prg -Prg_Path "C:\Programs\MyApp" -Win32AppsRootPath "C:\Win32AppsRoot"
    #>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, HelpMessage = "Provide the program object.")]
        [ValidateNotNullOrEmpty()]
        [pscustomobject]$Prg,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the source path of the program.")]
        [ValidateNotNullOrEmpty()]
        [string]$Prg_Path,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the root path for Win32 apps.")]
        [ValidateNotNullOrEmpty()]
        [string]$Win32AppsRootPath
    )

    # Check if the source path exists, create if it doesn't
    if (-not (Test-Path -Path $Prg_Path)) {
        Write-EnhancedLog -Message "Source path $Prg_Path does not exist. Creating it." -Level "INFO"
        New-Item -Path $Prg_Path -ItemType Directory -Force
    }
    
    # Prepare the destination root path and app-specific destination path
    $destinationRootPath = Join-Path -Path $Win32AppsRootPath -ChildPath "Win32Apps-published"
    if (-not (Test-Path -Path $destinationRootPath)) {
        New-Item -Path $destinationRootPath -ItemType Directory -Force
    }

    $destinationPath = Join-Path -Path $destinationRootPath -ChildPath $Prg.name
    if (-not (Test-Path -Path $destinationPath)) {
        New-Item -Path $destinationPath -ItemType Directory -Force
    }

    Write-EnhancedLog -Message "Destination path created: $destinationPath" -Level "INFO"

    # Return an object with the paths
    return [pscustomobject]@{
        Prg              = $Prg.name
        SourcePath       = $Prg_Path
        DestinationRoot  = $destinationRootPath
        DestinationPath  = $destinationPath
    }
}


# # Prepare paths and store the returned object
# $paths = Prepare-Paths -Prg $Prg -Prg_Path "C:\Programs\MyApp" -Win32AppsRootPath "C:\Win32AppsRoot"

# # Access the properties of the returned object
# Write-Host "Program Name: $($paths.Prg)"
# Write-Host "Source Path: $($paths.SourcePath)"
# Write-Host "Destination Root: $($paths.DestinationRoot)"
# Write-Host "Destination Path: $($paths.DestinationPath)"


# Program Name: MyApp
# Source Path: C:\Programs\MyApp
# Destination Root: C:\Win32AppsRoot\Win32Apps
# Destination Path: C:\Win32AppsRoot\Win32Apps\MyApp

#EndRegion '.\Public\Prepare-Paths.ps1' 81
#Region '.\Public\Process-Folder.ps1' -1

function Process-Folder {
    <#
    .SYNOPSIS
    Processes a folder by handling printer installations and Win32 app configurations.
 
    .DESCRIPTION
    This function processes a folder by checking for the presence of a printer configuration file (`printer.json`) and, if found, processes the printer installation. It also processes Win32 app configurations based on the provided folder and configuration object. The function returns an object with details about the folder processing and prints a summary report at the end with counts and color-coded statuses.
 
    .PARAMETER Folder
    The folder to be processed, which may contain a `printer.json` file for printer installation.
 
    .PARAMETER config
    The configuration object required for Win32 app processing.
 
    .PARAMETER Repo_winget
    The path to the repository (winget) where the program source resides.
 
    .PARAMETER scriptpath
    The path to the script that is being executed.
 
    .EXAMPLE
    $folderDetails = Process-Folder -Folder (Get-Item "C:\Apps\MyApp") -config $config -Repo_winget "C:\Repo\winget" -scriptpath "C:\path\to\script"
    Processes the folder and returns the folder processing details.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the folder to be processed.")]
        [ValidateNotNullOrEmpty()]
        [System.IO.DirectoryInfo]$Folder,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the configuration for the Win32 app.")]
        [ValidateNotNullOrEmpty()]
        [pscustomobject]$config,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the winget repository.")]
        [ValidateNotNullOrEmpty()]
        [string]$Repo_winget,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the script being executed.")]
        [ValidateNotNullOrEmpty()]
        [string]$scriptpath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Process-Folder function for folder: $($Folder.Name)" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Initialize counters and app status list
        $totalApps = 0
        $successfulApps = 0
        $failedApps = 0
        $appStatuses = [System.Collections.Generic.List[PSCustomObject]]::new()  # Efficient list initialization
    }

    Process {
        try {
            # Initialize a variable to track if printer installation was processed
            $printerProcessed = $false

            # Construct the path to the printer.json within the current folder
            $printerConfigPath = Join-Path -Path $Folder.FullName -ChildPath "printer.json"

            if (Test-Path -Path $printerConfigPath) {
                Write-EnhancedLog -Message "printer.json found in folder: $($Folder.Name). Processing printer installation." -Level "INFO"
                Process-PrinterInstallation -PrinterConfigPath $printerConfigPath
                Write-EnhancedLog -Message "Processed printer installation for folder: $($Folder.Name)" -Level "INFO"
                $printerProcessed = $true
            }
            else {
                Write-EnhancedLog -Message "printer.json not found in folder: $($Folder.Name)" -Level "WARNING"
            }

            # Process Win32 app and get the details
            Write-EnhancedLog -Message "Processing Win32 app configuration for folder: $($Folder.Name)" -Level "INFO"
            $totalApps++

            try {
                $appDetails = Process-Win32App -Folder $Folder -config $config -Repo_winget $Repo_winget -scriptpath $scriptpath
                Write-EnhancedLog -Message "Successfully processed Win32 app: $($Folder.Name)" -Level "INFO"
                $successfulApps++
                $appStatuses.Add([pscustomobject]@{ AppName = $Folder.Name; Status = "Success" })
            }
            catch {
                Write-EnhancedLog -Message "Failed to process Win32 app: $($Folder.Name)" -Level "ERROR"
                $failedApps++
                $appStatuses.Add([pscustomobject]@{ AppName = $Folder.Name; Status = "Failed" })
            }

            # Build the return object
            $folderDetails = [pscustomobject]@{
                FolderName       = $Folder.Name
                PrinterProcessed = $printerProcessed
                AppDetails       = $appDetails
            }

            return $folderDetails
        }
        catch {
            Write-EnhancedLog -Message "Error occurred during folder processing: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
    }

    End {
        Write-EnhancedLog -Message "Completed Process-Folder function for folder: $($Folder.Name)" -Level "INFO"

        # Print final summary report
        Write-Host "Final Summary Report" -ForegroundColor Green
        Write-Host "---------------------" -ForegroundColor Green
        Write-Host "Total Apps Processed: $totalApps" -ForegroundColor Green
        Write-Host "Successful Apps: $successfulApps" -ForegroundColor Green
        Write-Host "Failed Apps: $failedApps" -ForegroundColor Red

        # Loop through appStatuses for detailed report
        foreach ($appStatus in $appStatuses) {
            if ($appStatus.Status -eq "Success") {
                Write-Host "App: $($appStatus.AppName) - Status: $($appStatus.Status)" -ForegroundColor Green
            }
            else {
                Write-Host "App: $($appStatus.AppName) - Status: $($appStatus.Status)" -ForegroundColor Red
            }
        }
    }
}

# Example Usage:
# $folderDetails = Process-Folder -Folder (Get-Item "C:\Apps\MyApp") -config $config -Repo_winget "C:\Repo\winget" -scriptpath "C:\path\to\script"

#EndRegion '.\Public\Process-Folder.ps1' 131
#Region '.\Public\Process-PrinterInstallation.ps1' -1

function Process-PrinterInstallation {
    param (
        [Parameter(Mandatory = $true)]
        [string]$PrinterConfigPath
    )

    $commands = Invoke-PrinterInstallation -PrinterConfigPath $PrinterConfigPath -AppConfigPath $configPath
    Write-EnhancedLog -Message "Install Command: $($commands.InstallCommand)"
    Write-EnhancedLog -Message "Uninstall Command: $($commands.UninstallCommand)"
    
    $global:InstallCommandLine = $commands.InstallCommand
    $global:UninstallCommandLine = $commands.UninstallCommand
}
#EndRegion '.\Public\Process-PrinterInstallation.ps1' 14
#Region '.\Public\Process-Win32App.ps1' -1

function Process-Win32App {
    <#
    .SYNOPSIS
    Processes a Win32 app folder and uploads the app to Intune.
 
    .DESCRIPTION
    This function processes the Win32 app by defining the program's ID, name, and description based on the folder's name. It checks for a valid application image, defines the source path, and uploads the application to Intune using PowerShell 5. The function returns an object with details about the processed Win32 app.
 
    .PARAMETER Folder
    The folder containing the Win32 app to be processed.
 
    .PARAMETER config
    The configuration object required for uploading the Win32 app.
 
    .PARAMETER Repo_winget
    The path to the repository (winget) where the program source resides.
 
    .PARAMETER scriptpath
    The path to the script that is being executed.
 
    .EXAMPLE
    $appDetails = Process-Win32App -Folder (Get-Item "C:\Programs\MyApp") -config $config -Repo_winget "C:\Repo\winget" -scriptpath "C:\path\to\script"
    Processes the Win32 app and returns the app details.
    #>


    [CmdletBinding(ConfirmImpact = 'Medium')]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the folder containing the Win32 app.")]
        [ValidateNotNullOrEmpty()]
        [System.IO.DirectoryInfo]$Folder,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the configuration for the Win32 app.")]
        [ValidateNotNullOrEmpty()]
        [PSCustomObject]$config,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the winget repository.")]
        [ValidateNotNullOrEmpty()]
        [string]$Repo_winget,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the script being executed.")]
        [ValidateNotNullOrEmpty()]
        [string]$scriptpath
    )

    Begin {
        # Create the program object based on the folder's name
        $Prg = [PSCustomObject]@{
            id          = $Folder.Name
            name        = $Folder.Name
            Description = $Folder.Name
        }

        Write-EnhancedLog -Message "Program ID: $($Prg.id)" -Level "INFO"
        Write-EnhancedLog -Message "Program Name: $($Prg.name)" -Level "INFO"
        Write-EnhancedLog -Message "Description: $($Prg.Description)" -Level "INFO"

        # Ensure that Program ID matches Program Name
        if ($Prg.id -ne $Prg.name) {
            throw "Error: Program ID ('$($Prg.id)') does not match Program Name ('$($Prg.name)')."
        }
    }

    Process {
        # Define the source path for the program and check for the application image
        $sourcePathDetails = Define-SourcePath -Repo_winget $Repo_winget -Prg $Prg
        $Prg_Path = $sourcePathDetails.SourcePath
        $imageDetails = Check-ApplicationImage -Prg_Path $Prg_Path -Prg $Prg

        # Define the splatted parameters for Upload-Win32App
        $UploadWin32AppParams = @{

            Prg               = $Prg
            Prg_Path          = $sourcePathDetails.SourcePath
            Prg_img           = $imageDetails.ImagePath
            Win32AppsRootPath = $scriptpath
            config            = $config
        }
        Upload-Win32App @UploadWin32AppParams

        # Build the return object with details of the Win32 app
        $appDetails = [pscustomobject]@{
            ProgramID       = $Prg.id
            ProgramName     = $Prg.name
            SourcePath      = $sourcePathDetails.SourcePath
            Config          = $config
        }

        # Return the app details object
        return $appDetails
    }

    End {
        Write-EnhancedLog -Message "Completed processing of Win32 app: $($Prg.name)" -Level "INFO"
    }
}

# Example Usage:
# $appDetails = Process-Win32App -Folder (Get-Item "C:\Programs\MyApp") -config $config -Repo_winget "C:\Repo\winget" -scriptpath "C:\path\to\script"





# # Run Process-Win32App and store the returned object
# $appDetails = Process-Win32App -Folder (Get-Item "C:\Programs\MyApp") -config $config

# # Access the properties of the returned object
# Write-Host "Program ID: $($appDetails.ProgramID)"
# Write-Host "Program Name: $($appDetails.ProgramName)"
# Write-Host "Source Path: $($appDetails.SourcePath)"

# Program ID: MyApp
# Program Name: MyApp
# Source Path: C:\path\to\Win32Apps-DropBox\MyApp
#EndRegion '.\Public\Process-Win32App.ps1' 115
#Region '.\Public\Remove-IntuneWinFiles.ps1' -1

function Remove-IntuneWinFiles {
    <#
    .SYNOPSIS
    Removes all *.intuneWin files from a specified directory.
 
    .DESCRIPTION
    This function searches for all files with the .intuneWin extension
    in the specified directory and removes them. It logs actions taken
    and any errors encountered using the Write-EnhancedLog function.
 
    .PARAMETER DirectoryPath
    The path to the directory from which *.intuneWin files will be removed.
 
    .EXAMPLE
    Remove-IntuneWinFiles -DirectoryPath "d:\Users\aollivierre\AppData\Local\Intune-Win32-Deployer\apps-winget"
    Removes all *.intuneWin files from the specified directory and logs the actions.
 
    .NOTES
    Ensure you have the necessary permissions to delete files in the specified directory.
    #>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the directory path from which *.intuneWin files will be removed.")]
        [ValidateNotNullOrEmpty()]
        [string]$DirectoryPath
    )

    Begin {
        Write-EnhancedLog -Message "Starting to remove *.intuneWin files from $DirectoryPath recursively." -Level "INFO"

        # Validate directory existence
        if (-not (Test-Path -Path $DirectoryPath)) {
            Write-EnhancedLog -Message "Directory not found: $DirectoryPath" -Level "ERROR"
            throw "Directory not found: $DirectoryPath"
        }
    }

    Process {
        try {
            # Get the list of *.intuneWin files recursively
            Write-EnhancedLog -Message "Searching for *.intuneWin files in $DirectoryPath..." -Level "INFO"
            $files = Get-ChildItem -Path $DirectoryPath -Filter "*.intuneWin" -Recurse -ErrorAction Stop

            if ($files.Count -eq 0) {
                Write-EnhancedLog -Message "No *.intuneWin files found in $DirectoryPath." -Level "INFO"
            }
            else {
                foreach ($file in $files) {
                    if ($PSCmdlet.ShouldProcess($file.FullName, "Remove *.intuneWin file")) {
                        Remove-Item -Path $file.FullName -Force -ErrorAction Stop
                        Write-EnhancedLog -Message "Removed file: $($file.FullName)" -Level "INFO"
                    }
                }
            }
        }
        catch {
            Write-EnhancedLog -Message "Error removing *.intuneWin files: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_  # Re-throwing the error for further handling
        }
    }

    End {
        Write-EnhancedLog -Message "Completed removal of *.intuneWin files from $DirectoryPath." -Level "INFO"
    }
}
#EndRegion '.\Public\Remove-IntuneWinFiles.ps1' 68
#Region '.\Public\Set-AppIcon.ps1' -1


function Set-AppIcon {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Prg_img
    )

    $Icon = New-IntuneWin32AppIcon -FilePath $Prg_img
    Write-EnhancedLog -Message "App icon set - done" -Level "INFO"

    return $Icon
}
#EndRegion '.\Public\Set-AppIcon.ps1' 13
#Region '.\Public\Set-InstallCommandLine.ps1' -1

function Set-InstallCommandLine {
    param(
        [Parameter(Mandatory = $true)]
        [pscustomobject]$config
    )

    if ($config.serviceUIPSADT -eq $true) {
        $InstallCommandLine = "ServiceUI.exe -process:explorer.exe Deploy-Application.exe -DeploymentType install -Deploymode Interactive"
        $UninstallCommandLine = "ServiceUI.exe -process:explorer.exe Deploy-Application.exe -DeploymentType Uninstall -Deploymode Interactive"
    }
    elseif ($config.PSADT -eq $true) {
        $InstallCommandLine = "Deploy-Application.exe -DeploymentType install -DeployMode Interactive"
        $UninstallCommandLine = "Deploy-Application.exe -DeploymentType Uninstall -DeployMode Interactive"
    }
    else {
        $InstallCommandLine = "%SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -windowstyle hidden -executionpolicy bypass -command .\install.ps1"
        $UninstallCommandLine = "%SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -windowstyle hidden -executionpolicy bypass -command .\uninstall.ps1"
    }

    return @{
        InstallCommandLine   = $InstallCommandLine
        UninstallCommandLine = $UninstallCommandLine
    }
}
#EndRegion '.\Public\Set-InstallCommandLine.ps1' 25
#Region '.\Public\Setup-LinuxEnvironment.ps1' -1

# function Setup-LinuxEnvironment {
# # Get the base paths from the global variables
# Setup-Win32GlobalPaths

# # Import the module using the Linux path
# Import-Module $LinuxModulePath -Verbose

# # Convert paths from Windows to Linux format
# # $global:AOscriptDirectory = Convert-WindowsPathToLinuxPath -WindowsPath "$PSscriptroot"
# # $global:directoryPath = Convert-WindowsPathToLinuxPath -WindowsPath "$PSscriptroot\Win32Apps-DropBox"
# # $global:Repo_Path = Convert-WindowsPathToLinuxPath -WindowsPath "$PSscriptroot"
# $global:IntuneWin32App = Convert-WindowsPathToLinuxPath -WindowsPath "C:\Code\IntuneWin32App\IntuneWin32App.psm1"

# Import-Module $global:IntuneWin32App -Verbose -Global


# $global:AOscriptDirectory = "$PSscriptroot"
# $global:directoryPath = "$PSscriptroot/Win32Apps-DropBox"
# $global:Repo_Path = "$PSscriptroot"
# $global:Repo_winget = "$global:Repo_Path/Win32Apps-DropBox"
# }
#EndRegion '.\Public\Setup-LinuxEnvironment.ps1' 22
#Region '.\Public\Setup-Win32GlobalPaths.ps1' -1

function Setup-Win32GlobalPaths {
    <#
    .SYNOPSIS
    Sets up the global paths based on the environment.
 
    .DESCRIPTION
    This function sets the global paths dynamically based on whether the environment is running inside Docker or not. It uses environment variables when in Docker, otherwise defaults to the script root path or a provided script path.
 
    .PARAMETER scriptpath
    The path to the script that is being executed, used to determine the base paths.
 
    .EXAMPLE
    Setup-Win32GlobalPaths -scriptpath "C:\path\to\script.ps1"
    Sets up the global paths using the provided script path.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false, HelpMessage = "Provide the path to the script being executed.")]
        [ValidateNotNullOrEmpty()]
        [string]$scriptpath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Setup-Win32GlobalPaths function" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            if ($env:DOCKER_ENV -eq $true) {
                Write-EnhancedLog -Message "Docker environment detected." -Level "INFO"
                $global:scriptBasePath = $env:SCRIPT_BASE_PATH
                Write-EnhancedLog -Message "Docker script base path: $global:scriptBasePath" -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "Non-Docker environment detected." -Level "INFO"

                # Use provided scriptpath if available, otherwise default to $PSScriptRoot
                if ($scriptpath) {
                    $global:scriptBasePath = Split-Path -Path $scriptpath -Parent
                    Write-EnhancedLog -Message "Using provided script path: $global:scriptBasePath" -Level "INFO"
                }
                else {
                    $global:scriptBasePath = $PSScriptRoot
                    Write-EnhancedLog -Message "Using PSScriptRoot as script base path: $global:scriptBasePath" -Level "INFO"
                }
            }
        }
        catch {
            Write-EnhancedLog -Message "Error occurred during Setup-Win32GlobalPaths: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
    }

    End {
        Write-EnhancedLog -Message "Completed Setup-Win32GlobalPaths function." -Level "INFO"
    }
}
#EndRegion '.\Public\Setup-Win32GlobalPaths.ps1' 61
#Region '.\Public\Setup-WindowsEnvironment.ps1' -1

function Setup-WindowsEnvironment {
    <#
    .SYNOPSIS
    Sets up the Windows environment by configuring necessary paths and logging the setup process.
 
    .DESCRIPTION
    This function dynamically constructs the necessary paths for the Windows environment using the provided script path. It logs the setup process and returns an object with the relevant paths.
 
    .PARAMETER scriptpath
    The path to the script that is being executed, used to determine the base paths for the setup.
 
    .EXAMPLE
    $envDetails = Setup-WindowsEnvironment -scriptpath "C:\path\to\script.ps1"
    Initializes the Windows environment using the provided script path and returns the paths.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the path to the script being executed.")]
        [ValidateNotNullOrEmpty()]
        [string]$scriptpath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Setup-WindowsEnvironment function" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        try {
            # Set base paths from script path
            Write-EnhancedLog -Message "Script base path: $scriptpath" -Level "INFO"

            # Construct the paths dynamically using the base path
            $AOscriptDirectory = Join-Path -Path $scriptpath -ChildPath "Win32Apps-Source"
            $directoryPath = Join-Path -Path $scriptpath -ChildPath "Win32Apps-Source"
            $Repo_winget = Join-Path -Path $scriptpath -ChildPath "Win32Apps-Source"

            # Log the dynamically constructed paths
            Log-Params -Params @{

                AOscriptDirectory = $AOscriptDirectory
                directoryPath     = $directoryPath
                Repo_Path         = $scriptpath
                Repo_winget       = $Repo_winget
            }

            Write-EnhancedLog -Message "Paths set up successfully." -Level "INFO"

            # Return the constructed paths as an object
            return [pscustomobject]@{
                AOscriptDirectory = $AOscriptDirectory
                directoryPath     = $directoryPath
                Repo_Path         = $scriptpath
                Repo_winget       = $Repo_winget
            }
        }
        catch {
            Write-EnhancedLog -Message "Error occurred during Setup-WindowsEnvironment: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
    }

    End {
        Write-EnhancedLog -Message "Completed Setup-WindowsEnvironment function." -Level "INFO"
    }
}


# # Run Setup-WindowsEnvironment and store the returned object
# $envDetails = Setup-WindowsEnvironment -scriptpath "C:\path\to\script.ps1"

# # Access the properties of the returned object
# Write-Host "AO Script Directory: $($envDetails.AOscriptDirectory)"
# Write-Host "Directory Path: $($envDetails.directoryPath)"
# Write-Host "Repository Path: $($envDetails.Repo_Path)"
# Write-Host "Winget Path: $($envDetails.Repo_winget)"

# AO Script Directory: C:\path\to\Win32Apps-Source
# Directory Path: C:\path\to\Win32Apps-Source
# Repository Path: C:\path\to\script.ps1
# Winget Path: C:\path\to\Win32Apps-Source
#EndRegion '.\Public\Setup-WindowsEnvironment.ps1' 82
#Region '.\Public\Upload-IntuneWinPackage.ps1' -1

function Upload-IntuneWinPackage {
    <#
    .SYNOPSIS
    Uploads the Win32 Intune package and assigns it to all users.
 
    .DESCRIPTION
    This function uploads a specified Win32 Intune package, logs the process, and assigns the app to all users. It handles errors and logs all parameters excluding sensitive data like the app icon.
 
    .PARAMETER Prg
    The program object containing metadata like the name of the program.
 
    .PARAMETER Prg_Path
    The path where the program resides.
 
    .PARAMETER Prg_img
    The path to the program's image.
 
    .PARAMETER config
    The configuration object required for uploading the Win32 Intune package.
 
    .PARAMETER IntuneWinFile
    The path to the .intunewin file.
 
    .PARAMETER InstallCommandLine
    The command line to install the app.
 
    .PARAMETER UninstallCommandLine
    The command line to uninstall the app.
 
    .EXAMPLE
    Upload-IntuneWinPackage -Prg $Prg -Prg_Path "C:\Path\To\App" -Prg_img "C:\Path\To\Image.png" -config $config -IntuneWinFile "C:\Path\To\Package.intunewin" -InstallCommandLine "install.cmd" -UninstallCommandLine "uninstall.cmd"
    Uploads the Win32 Intune package and assigns it to all users.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the program object.")]
        [ValidateNotNullOrEmpty()]
        [pscustomobject]$Prg,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the program path.")]
        [ValidateNotNullOrEmpty()]
        [string]$Prg_Path,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the program image.")]
        [ValidateNotNullOrEmpty()]
        [string]$Prg_img,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the configuration object.")]
        [ValidateNotNullOrEmpty()]
        [pscustomobject]$config,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the IntuneWin file path.")]
        [ValidateNotNullOrEmpty()]
        [string]$IntuneWinFile,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the install command line.")]
        [ValidateNotNullOrEmpty()]
        [string]$InstallCommandLine,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the uninstall command line.")]
        [ValidateNotNullOrEmpty()]
        [string]$UninstallCommandLine
    )

    Begin {
        Write-EnhancedLog -Message "Starting Upload-IntuneWinPackage function for program: $($Prg.Name)" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Set display name
            $DisplayName = "$($Prg.Name)"
            Write-EnhancedLog -Message "DisplayName set: $DisplayName" -Level "INFO"

            # Create detection rule, requirement rule, and set app icon
            $DetectionRule = Create-DetectionRule -Prg_Path $Prg_Path
            $RequirementRule = Create-RequirementRule
            $Icon = Set-AppIcon -Prg_img $Prg_img

            # Splatting for Intune app parameters
            $IntuneAppParams = @{
                FilePath                 = $IntuneWinFile
                Icon                     = $Icon
                DisplayName              = "$DisplayName ($($config.InstallExperience))"
                Description              = "$DisplayName ($($config.InstallExperience))"
                Publisher                = $config.Publisher
                AppVersion               = $config.AppVersion
                Developer                = $config.Developer
                Owner                    = $config.Owner
                CompanyPortalFeaturedApp = [System.Convert]::ToBoolean($config.CompanyPortalFeaturedApp)
                InstallCommandLine       = $InstallCommandLine
                UninstallCommandLine     = $UninstallCommandLine
                InstallExperience        = $config.InstallExperience
                RestartBehavior          = $config.RestartBehavior
                DetectionRule            = $DetectionRule
                RequirementRule          = $RequirementRule
                InformationURL           = $config.InformationURL
                PrivacyURL               = $config.PrivacyURL
                Verbose                  = $true
            }

            # Log the parameters excluding the Icon
            $IntuneAppParamsForLogging = [ordered]@{}
            foreach ($key in $IntuneAppParams.Keys) {
                if ($key -ne 'Icon') {
                    $IntuneAppParamsForLogging[$key] = $IntuneAppParams[$key]
                }
            }
            Log-Params -Params $IntuneAppParamsForLogging

            Write-EnhancedLog -Message "Calling Add-IntuneWin32App with IntuneAppParams - in progress" -Level "WARNING"
            # Reset-ModulePaths
            $Win32App = Add-IntuneWin32App @IntuneAppParams
            #TODO add more logging here to check if Running in PS7 when PS5 is launched from PS7 because it giving a null value error which usually happens when you are running from PS7 as Add-IntuneWin32App uses star-process to start a new session so we might need to call Reset-ModulePaths within a modified version of the Add-IntuneWin32App script and then build a script to copy the modified version of this into the Windows PowerShell Modules folder just like how we are doing with the other function Copy-InvokeAzureStorageBlobUploadFinalize
            Write-EnhancedLog -Message "Win32 app added successfully. App ID: $($Win32App.id)" -Level "INFO"

            Write-EnhancedLog -Message "Assigning Win32 app to all users..." -Level "WARNING"
            Add-IntuneWin32AppAssignmentAllUsers -ID $Win32App.id -Intent "available" -Notification "showAll" -Verbose
            Write-EnhancedLog -Message "Assignment completed successfully." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "Error during IntuneWin32 app process: $($_.Exception.Message)" -Level "ERROR"
            Write-Host "Error during IntuneWin32 app process: $($_.Exception.Message)" -ForegroundColor Red
            exit
        }
    }

    End {
        Write-EnhancedLog -Message "Completed Upload-IntuneWinPackage function for program: $($Prg.Name)" -Level "INFO"
    }
}
#EndRegion '.\Public\Upload-IntuneWinPackage.ps1' 134
#Region '.\Public\Upload-Win32App.ps1' -1

function Upload-Win32App {
    <#
    .SYNOPSIS
    Uploads a Win32 application to Intune.
 
    .DESCRIPTION
    The Upload-Win32App function uploads a Win32 application package to Intune, prepares paths, creates the IntuneWin package, and uploads it along with the install and uninstall command lines. It validates that the script is running in PowerShell 5 before proceeding.
 
    .PARAMETER Prg
    The application object with necessary details for the upload.
 
    .PARAMETER Prg_Path
    The path to the application being uploaded.
 
    .PARAMETER Prg_img
    The image associated with the application (optional).
 
    .PARAMETER Win32AppsRootPath
    The root path where the Win32 apps are stored (optional).
 
    .PARAMETER linetoadd
    Additional lines to add (optional).
 
    .PARAMETER config
    Configuration object containing necessary details for installation commands.
 
    .EXAMPLE
    $params = @{
        Prg = [pscustomobject]@{ name = 'ExampleApp'; id = 'exampleApp' }
        Prg_Path = "C:\Programs\ExampleApp"
        config = [pscustomobject]@{ InstallCommand = 'install.ps1' }
    }
    Upload-Win32App @params
    Uploads the specified Win32 app to Intune.
    #>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, HelpMessage = "Provide the application object containing necessary details.")]
        [ValidateNotNullOrEmpty()]
        [pscustomobject]$Prg,
    
        [Parameter(Mandatory = $true, HelpMessage = "Specify the path to the program.")]
        [ValidateNotNullOrEmpty()]
        [string]$Prg_Path,
    
        [Parameter(HelpMessage = "Specify the image associated with the application (optional).")]
        [string]$Prg_img,
    
        [Parameter(HelpMessage = "Specify the root path where the Win32 apps are stored (optional).")]
        [string]$Win32AppsRootPath,
    
        [Parameter(HelpMessage = "Provide any additional lines to add (optional).")]
        [string]$linetoadd,
    
        [Parameter(Mandatory = $true, HelpMessage = "Provide the configuration object for command lines.")]
        [ValidateNotNullOrEmpty()]
        [pscustomobject]$config
    )
    

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

        # Check if running in PowerShell 5
        if ($PSVersionTable.PSVersion.Major -ne 5) {
            Write-EnhancedLog -Message "This script must be run in PowerShell 5. Please switch to PowerShell 5 and rerun the script." -Level "ERROR"
            throw "PowerShell 5 is required for this operation. Halting script execution."
        }

        Write-EnhancedLog -Message "Validated PowerShell 5 environment" -Level "INFO"
    }

    Process {
        try {
            if ($PSCmdlet.ShouldProcess("Win32 app: $($Prg.name)", "Upload to Intune")) {
                Write-EnhancedLog -Message "Uploading: $($Prg.name)" -Level "WARNING"
    
                # Set the install and uninstall command lines
                $InstallCommandLines = Set-InstallCommandLine -config $config
    
                # Log parameters
                Log-Params -Params @{
                    Prg      = $Prg
                    Prg_Path = $Prg_Path
                    Prg_img  = $Prg_img
                }
    
                # Prepare paths for the application
                $paths = Prepare-Paths -Prg $Prg -Prg_Path $Prg_Path -Win32AppsRootPath $Win32AppsRootPath
    
                # Splatting for Create-IntuneWinPackage
                $createIntuneWinParams = @{
                    Prg             = $Prg
                    Prg_Path        = $Prg_Path
                    destinationPath = $paths.destinationPath
                }
                $IntuneWinFile = Create-IntuneWinPackage @createIntuneWinParams
    
                # Splatting for Upload-IntuneWinPackage
                $uploadParams = @{
                    Prg                  = $Prg
                    Prg_Path             = $Prg_Path
                    Prg_img              = $Prg_img
                    config               = $config
                    IntuneWinFile        = $IntuneWinFile
                    InstallCommandLine   = $InstallCommandLines.InstallCommandLine
                    UninstallCommandLine = $InstallCommandLines.UninstallCommandLine
                }
                Upload-IntuneWinPackage @uploadParams
            }
            else {
                Write-EnhancedLog -Message "Operation skipped due to WhatIf or confirmation." -Level "INFO"
            }
        }
        catch {
            Write-EnhancedLog -Message "Error during Upload-Win32App: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
        finally {
            Write-EnhancedLog -Message "Exiting Upload-Win32App function" -Level "Notice"
        }
    }
    

    End {
        Write-EnhancedLog -Message "Upload-Win32App completed successfully for $($Prg.name)" -Level "INFO"
    }
}
#EndRegion '.\Public\Upload-Win32App.ps1' 132