Private/New-IntuneFramework.ps1

<#
.Synopsis
Created on: 24/03/2024
Updated on: 08/01/2025
Created by: Ben Whitmore
Filename: New-Win32appFramework.ps1
 
.Description
Function to create a Win32 app JSON framework
Parameter descriptions reference https://learn.microsoft.com/en-us/mem/intune/apps/apps-win32-add
 
.PARAMETER LogId
The component (script name) passed as LogID to the 'Write-Log' function.
This parameter is built from the line number of the call from the function up the pipeline
 
.PARAMETER Name
Enter the name of the app as it appears in the company portal. Make sure all app names
that you use are unique. If the same app name exists twice, only one of the apps appears
in the company portal
 
.PARAMETER Description
Enter the description of the app. The description appears in the company portal
 
.PARAMETER Publisher
Enter the name of the publisher of the app
 
.PARAMETER AppVersion
The version of the app
 
.PARAMETER InformationURL
The URL of a website that contains information about this app. The URL
appears in the company portal
 
.PARAMETER PrivacyURL
The URL of a website that contains privacy information for this app.
 
.PARAMETER Notes
Enter any notes that you want to associate with this app
 
.PARAMETER LargeIcon
The base64 value of the icon of the app
 
.PARAMETER Path
Path to the Win32apps folder
 
.PARAMETER FileName
The name of the Win32app filename value
 
.PARAMETER SetupFile
The name of the setup file
 
.PARAMETER InstallCommandLine
The install command line for the app
 
.PARAMETER UninstallCommandLine
The uninstall command line for the app
 
.PARAMETER UninstallCommandLineFallback
The uninstall command line for the app if one is not provided. We set this to the InstallCommandLine by default. You could use "cmd /c" (which does nothing)
 
.PARAMETER DetectionMethodJson
The JSON body for the detection method of the app
 
.PARAMETER DetectionScript
The base64 value of the detection method script for the app
 
.PARAMETER DetectionScriptType
The detection method type for the app
 
.PARAMETER DetectionMethodMSI
The MSI product information for detection
 
.PARAMETER MinimumOSArchitecture
The minimum operating system architecture required for the app
 
.PARAMETER MinimumOSVersion
The minimum operating system version required for the app
 
.PARAMETER InstallExperience
System or User
 
.PARAMETER RestartBehavior
The default restart behavior for the app
 
.PARAMETER MaxExecutionTime
The maximum time (in minutes) that the app is expected to take to execute
 
.PARAMETER AllowAvailableUninstall
When creating the Win32App, allow the user to uninstall the app if it is available in the Company Portal
 
.PARAMETER ReturnCodeMap
Hash table of default return codes
 
#>

function New-IntuneWinFramework {
    param(
        [Parameter(Mandatory = $false, ValuefromPipeline = $false, HelpMessage = 'The component (script name) passed as LogID to the Write-Log function')]
        [string]$LogId = $($MyInvocation.MyCommand).Name,
        [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 0, HelpMessage = "The unique app name as it appears in the company portal")]
        [string]$Name,
        [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 1, HelpMessage = "The app description for the company portal")]
        [string]$Description,
        [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 2, HelpMessage = "The publisher name of the app")]
        [string]$Publisher,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 3, HelpMessage = "The app version of the app")]
        [string]$AppVersion,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 4, HelpMessage = "The information URL of the app")]
        [string]$InformationURL,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 5, HelpMessage = "The privacy URL of the app")]
        [string]$PrivacyURL,
        [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 6, HelpMessage = "The notes associated with the app")]
        [string]$Notes,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 7, HelpMessage = "The base64 value of the icon of the app")]
        [string]$LargeIcon,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 8, HelpMessage = "Path to the Win32apps folder")]
        [string]$Path,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 9, HelpMessage = "The win32app filename value")]
        [string]$FileName,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 10, HelpMessage = "The name of the setupfile")]
        [string]$SetupFile,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 11, HelpMessage = "The install command line for the app")]
        [string]$InstallCommandLine,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 12, HelpMessage = "The uninstall command line for the app")]
        [string]$UninstallCommandLine,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 13, HelpMessage = "The uninstall command line for the app")]
        [string]$UninstallCommandLineFallback = $InstallCommandLine,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 14, HelpMessage = "The JSON body for the detection method of the app")]
        [string]$DetectionMethodJson,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 15, HelpMessage = "The base64 value of the detection method script for the app")]
        [string]$DetectionScript,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 16, HelpMessage = "The MSI product information for detection")]
        [object]$DetectionMethodMSI,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 17, HelpMessage = "The minimum operating system architecture required for the app")]
        [string]$MinimumOSArchitecture = 'x64,x86',
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 18, HelpMessage = "The minimum operating system version required for the app")]
        [string]$MinimumOSVersion = "Windows10_21H2",
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 19, HelpMessage = "System or User")]
        [ValidateSet('System', 'User')]
        [string]$InstallExperience,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 20, HelpMessage = "The mdefault restart behavior for the app")]
        [ValidateSet("allow", "basedOnReturnCode", "suppress", "force")]
        [string]$RestartBehavior = "basedOnReturnCode",
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 21, HelpMessage = "The maximum time (in minutes) that the app is expected to take to execute")]
        [int]$MaxExecutionTime = 60,
        [Parameter(Mandatory = $false, ValuefromPipeline = $false, Position = 22, HelpMessage = "When creating the Win32App, allow the user to uninstall the app if it is available in the Company Portal")]
        [bool]$AllowAvailableUninstall,
        [Parameter(Mandatory = $false, ValuefromPipeline = $false, Position = 23, HelpMessage = "Json string of default return codes")]
        [string]$ReturnCodes = '{"0": "success","1707": "success","3010": "softReboot","1641": "hardReboot","1618": "retry"}'
    )

    begin {

        Write-LogAndHost -Message "Function: New-Win32appFramework was called" -LogId $LogId -ForegroundColor Cyan
        Write-LogAndHost -Message ("Processing JSON body for '{0}'" -f $Name) -LogId $LogId -ForegroundColor Cyan

        # Convert the JSON to an array of objects
        [object]$parsedJson = $ReturnCodes | ConvertFrom-Json
        [object]$returnCodesOrdered = foreach ($property in $parsedJson.PSObject.Properties) {
            [PSCustomObject]@{
                returnCode = [int]$property.Name
                type       = [string]$property.Value
            }
        }
    }

    process {
            
        # Create the JSON body for the Win32 app
        $body = [ordered]@{
            '@odata.type'                  = "#microsoft.graph.win32LobApp"
            displayName                    = $Name
            description                    = $Description
            publisher                      = $Publisher
            displayVersion                 = $AppVersion
            informationUrl                 = $InformationURL
            privacyInformationUrl          = $PrivacyURL
            notes                          = $Notes
            installExperience              = [ordered]@{
                '@odata.type'           = "#microsoft.graph.win32LobAppInstallExperience"
                "runAsAccount"          = $InstallExperience
                "deviceRestartBehavior" = $RestartBehavior
                "maxRunTimeInMinutes"   = $MaxExecutionTime
            }
            largeIcon                      = [ordered]@{
                '@odata.type' = "#microsoft.graph.mimeContent"
                type          = "image/png"
                value         = $LargeIcon
            }
            fileName                       = $FileName
            setupFilePath                  = $SetupFile
            installCommandLine             = $deploymentType.InstallCommandLine
            uninstallCommandLine           = if ([string]::IsNullOrEmpty($deploymentType.UninstallCommandLine)) { $UninstallCommandLineFallback } else { $deploymentType.UninstallCommandLine }
            minimumSupportedWindowsRelease = $MinimumOSVersion
            applicableArchitectures        = $MinimumOSArchitecture
        }

        # Allow available uninstall
        if ($AllowAvailableUninstall) {
            $body['allowAvailableUninstall'] = $true
        }

        # Add returns codes
        $body['returnCodes'] = $returnCodesOrdered

        # Detection method (rules) for the Win32 app
        $transformedRules = @()

        if ($PSBoundParameters.ContainsKey('DetectionMethodJson')) {
            Write-LogAndHost -Message "Using JSON method for building detection" -LogId $LogId -ForegroundColor Cyan
            $jsonObject = $DetectionMethodJson | ConvertFrom-Json

            # Transform the detection rules from the JSON object
            foreach ($rule in $jsonObject) {

                # File system detection
                if ($rule.'@odata.type' -eq "#microsoft.graph.win32LobAppFileSystemDetection") {
                    $transformedRules += [ordered]@{
                        '@odata.type'        = "microsoft.graph.win32LobAppFileSystemRule"
                        ruleType             = 'detection'
                        check32BitOn64System = $rule.check32BitOn64System
                        operationType        = $rule.detectionType
                        comparisonValue      = $rule.detectionValue
                        fileOrFolderName     = $rule.fileOrFolderName
                        operator             = $rule.operator
                        path                 = $rule.path
                    }
                }
                # Registry detection
                elseif ($rule.'@odata.type' -eq "#microsoft.graph.win32LobAppRegistryDetection") {
                    $transformedRules += [ordered]@{
                        '@odata.type'        = "microsoft.graph.win32LobAppRegistryRule"
                        ruleType             = 'detection'
                        check32BitOn64System = $rule.check32BitOn64System
                        operationType        = $rule.detectionType
                        comparisonValue      = $rule.detectionValue
                        keyPath              = $rule.keyPath
                        operator             = $rule.operator
                        valueName            = $rule.valueName
                    }
                }
                # MSI Detection
                elseif ($rule.'@odata.type' -eq "#microsoft.graph.win32LobAppProductCodeDetection") {
                    $transformedRules += [ordered]@{
                        '@odata.type'          = "#microsoft.graph.win32LobAppProductCodeRule"
                        productCode            = $rule.productCode
                        productVersion         = $rule.productVersion
                        productVersionOperator = $rule.productVersionOperator
                    }
                }
            }
        }
        elseif ($PSBoundParameters.ContainsKey('DetectionScript')) {
            Write-LogAndHost -Message "Using script method for building detection" -LogId $LogId -ForegroundColor Cyan
            $transformedRules += [ordered]@{
                '@odata.type'           = "#microsoft.graph.win32LobAppPowerShellScriptRule"
                'scriptContent'         = $DetectionScript
                'enforceSignatureCheck' = $false
                'runAs32Bit'            = $false
            }
        }
        

        $body['rules'] = $transformedRules

        # Install experience
        $body['installExperience'] = @{
            "@odata.type" = "microsoft.graph.win32LobAppInstallExperience"
            runAsAccount  = $InstallExperience
        }

        # Create JSON body
        $json = $body | ConvertTo-Json -Depth 5 -Compress

        # Write-Host JSON body but exclude largeIcon and ScriptContent values
        $jsonObject = $body

        if ($jsonObject.Contains("largeIcon")) {
            $jsonObject["largeIcon"] = "Base64IcondDataPresentButOmittedFromOutputDuetoSize"
        }
        if ($jsonObject.Contains("rules") -and $jsonObject["rules"][0].Contains("scriptContent")) {
            $jsonObject["rules"][0]["scriptContent"] = "Base64ScriptDataPresentButOmittedFromOutputDuetoSize"
        }

        Write-LogAndHost -Message "JSON body created" -LogId $LogId -ForegroundColor Cyan
        $jsonOutput = $jsonObject | ConvertTo-Json -Depth 5 -Compress
        Write-LogAndHost -Message ("'{0}'" -f $jsonOutput) -LogId $LogId -ForegroundColor Green
            
        # Write the JSON body to a file
        $jsonFile = Join-Path -Path $Path -ChildPath "Win32appBody.json"
        Write-LogAndHost -Message ("Writing JSON body to '{0}'" -f $jsonFile) -LogId $LogId -ForegroundColor Cyan -NewLine

        try {

            # Write the JSON body to the file
            [System.IO.File]::WriteAllText($jsonFile, $json, [System.Text.Encoding]::UTF8)
            Write-LogAndHost -Message ("Successfully wrote JSON body to '{0}'" -f $jsonFile) -LogId $LogId -ForegroundColor Green

            return $json
        }
        catch {
            Write-LogAndHost -Message ("Failed to write JSON body to '{0}'" -f $jsonFile) -LogId $LogId -Severity 3

            return $false
        }
    }
}