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" # Use New-IntuneWinPackage to create the package # New-IntuneWinPackage -SourcePath $Prg_Path -DestinationPath $destinationPath -SetupFile $setupFile -Verbose # $Win32AppPackage = New-IntuneWin32AppPackage -SourceFolder $Prg_Path -SetupFile $setupFile -OutputFolder $destinationPath -Verbose -Force:$true # 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' 95 #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" $Win32App = Add-IntuneWin32App @IntuneAppParams 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' 132 #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 |