From 66886b4bc2620566d433d84725bb97f4662f9227 Mon Sep 17 00:00:00 2001 From: Jasper Metselaar Date: Fri, 22 Dec 2023 14:05:09 +0100 Subject: [PATCH 1/2] Added detection and requirement rules to the Set-IntuneWin32Apps.ps1 --- Public/Set-IntuneWin32App.ps1 | 42 ++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/Public/Set-IntuneWin32App.ps1 b/Public/Set-IntuneWin32App.ps1 index 8a78f1b..2aee0a0 100644 --- a/Public/Set-IntuneWin32App.ps1 +++ b/Public/Set-IntuneWin32App.ps1 @@ -39,6 +39,16 @@ function Set-IntuneWin32App { .PARAMETER CompanyPortalFeaturedApp Specify whether to have the Win32 application featured in Company Portal or not. + .PARAMETER DetectionRule + Provide an array of a single or multiple OrderedDictionary objects as detection rules that will be used for the Win32 application. + + .PARAMETER RequirementRule + Provide an OrderedDictionary object as requirement rule that will be used for the Win32 application. + + .PARAMETER AdditionalRequirementRule + Provide an array of OrderedDictionary objects as additional requirement rule, e.g. for file, registry or script rules, that will be used for the Win32 application. + + .NOTES Author: Nickolaj Andersen Contact: @NickolajA @@ -96,7 +106,19 @@ function Set-IntuneWin32App { [bool]$CompanyPortalFeaturedApp, [parameter(Mandatory = $false, HelpMessage = "Specify whether to allow the Win32 application to be uninstalled from the Company Portal app when assigned as available.")] - [bool]$AllowAvailableUninstall + [bool]$AllowAvailableUninstall, + + [parameter(Mandatory = $false, HelpMessage = "Provide an array of a single or multiple OrderedDictionary objects as detection rules that will be used for the Win32 application.")] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.OrderedDictionary[]]$DetectionRule, + + [parameter(Mandatory = $false, HelpMessage = "Provide an OrderedDictionary object as requirement rule that will be used for the Win32 application.")] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.OrderedDictionary]$RequirementRule, + + [parameter(Mandatory = $false, HelpMessage = "Provide an array of OrderedDictionary objects as additional requirement rule, e.g. for file, registry or script rules, that will be used for the Win32 application.")] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.OrderedDictionary[]]$AdditionalRequirementRule ) Begin { # Ensure required authentication header variable exists @@ -158,6 +180,24 @@ function Set-IntuneWin32App { if ($PSBoundParameters["AllowAvailableUninstall"]) { $Win32AppBody.Add("allowAvailableUninstall", $AllowAvailableUninstall) } + if ($PSBoundParameters["DetectionRule"]) { + # Validate that correct detection rules have been passed on command line, only 1 PowerShell script based detection rule is allowed + if (($DetectionRule.'@odata.type' -contains "#microsoft.graph.win32LobAppPowerShellScriptDetection") -and (@($DetectionRules).'@odata.type'.Count -gt 1)) { + Write-Warning -Message "Multiple PowerShell Script detection rules were detected, this is not a supported configuration"; break + } + + # Add detection rules to Win32 app body object + Write-Verbose -Message "Detection rule objects passed validation checks, attempting to add to existing Win32 app body" + $Win32AppBody.Add("detectionRules", $DetectionRule) + } + + if ($PSBoundParameters["RequirementRule"]) { + $Win32AppBody.Add("RequirementRule", $RequirementRule) + } + + if ($PSBoundParameters["AdditionalRequirementRule"]) { + $Win32AppBody.Add("requirementRules", $AdditionalRequirementRule) + } try { # Attempt to call Graph and update Win32 app From adee532059fa876774471a06de1d2356eaa21e82 Mon Sep 17 00:00:00 2001 From: Jasper Metselaar Date: Mon, 29 Jan 2024 11:51:21 +0100 Subject: [PATCH 2/2] Added AZCopy support to Update-IntuneWin32AppPackageFile --- Public/Update-IntuneWin32AppPackageFile.ps1 | 103 +++++++++++++------- 1 file changed, 70 insertions(+), 33 deletions(-) diff --git a/Public/Update-IntuneWin32AppPackageFile.ps1 b/Public/Update-IntuneWin32AppPackageFile.ps1 index ac2a369..b0264eb 100644 --- a/Public/Update-IntuneWin32AppPackageFile.ps1 +++ b/Public/Update-IntuneWin32AppPackageFile.ps1 @@ -35,27 +35,36 @@ function Update-IntuneWin32AppPackageFile { [parameter(Mandatory = $true, HelpMessage = "Specify a local path to where the win32 app .intunewin file is located.")] [ValidateNotNullOrEmpty()] [ValidateScript({ - # Check if file name contains any invalid characters - if ((Split-Path -Path $_ -Leaf).IndexOfAny([IO.Path]::GetInvalidFileNameChars()) -ge 0) { - throw "File name '$(Split-Path -Path $_ -Leaf)' contains invalid characters" - } - else { - # Check if full path exist - if (Test-Path -Path $_) { - # Check if file extension is intunewin - if ([System.IO.Path]::GetExtension((Split-Path -Path $_ -Leaf)) -like ".intunewin") { - return $true + # Check if file name contains any invalid characters + if ((Split-Path -Path $_ -Leaf).IndexOfAny([IO.Path]::GetInvalidFileNameChars()) -ge 0) { + throw "File name '$(Split-Path -Path $_ -Leaf)' contains invalid characters" + } + else { + # Check if full path exist + if (Test-Path -Path $_) { + # Check if file extension is intunewin + if ([System.IO.Path]::GetExtension((Split-Path -Path $_ -Leaf)) -like ".intunewin") { + return $true + } + else { + throw "Given file name '$(Split-Path -Path $_ -Leaf)' contains an unsupported file extension. Supported extension is '.intunewin'" + } } else { - throw "Given file name '$(Split-Path -Path $_ -Leaf)' contains an unsupported file extension. Supported extension is '.intunewin'" + throw "File or folder does not exist" } } - else { - throw "File or folder does not exist" - } - } - })] - [string]$FilePath + })] + [string]$FilePath, + + [parameter(Mandatory = $false, HelpMessage = "Specify the UseAzCopy parameter switch when adding an application with source files larger than 500MB.")] + [ValidateNotNullOrEmpty()] + [switch]$UseAzCopy, + + [parameter(Mandatory = $false, HelpMessage = "Specify whether the AzCopy content transfer progress should use -WindowStyle Hidden or -NoNewWindow parameters for Start-Process. NoNewWindow will show transfer output, Hidden will not show progress but will support multi-threaded jobs.")] + [ValidateNotNullOrEmpty()] + [ValidateSet("Hidden", "NoNewWindow")] + [string]$AzCopyWindowStyle = "NoNewWindow" ) Begin { # Ensure required authentication header variable exists @@ -97,12 +106,12 @@ function Update-IntuneWin32AppPackageFile { # Create a new file entry in Intune for the upload of the .intunewin file Write-Verbose -Message "Constructing Win32 app content file body for uploading of .intunewin file" $Win32AppFileBody = [ordered]@{ - "@odata.type" = "#microsoft.graph.mobileAppContentFile" - "name" = $IntuneWinXMLMetaData.ApplicationInfo.FileName - "size" = [int64]$IntuneWinXMLMetaData.ApplicationInfo.UnencryptedContentSize + "@odata.type" = "#microsoft.graph.mobileAppContentFile" + "name" = $IntuneWinXMLMetaData.ApplicationInfo.FileName + "size" = [int64]$IntuneWinXMLMetaData.ApplicationInfo.UnencryptedContentSize "sizeEncrypted" = (Get-Item -Path $IntuneWinFilePath).Length - "manifest" = $null - "isDependency" = $false + "manifest" = $null + "isDependency" = $false } # Create the contentVersions files resource @@ -115,19 +124,47 @@ function Update-IntuneWin32AppPackageFile { Write-Verbose -Message "Waiting for Intune service to process contentVersions/files request" $FilesUri = "mobileApps/$($Win32App.id)/microsoft.graph.win32LobApp/contentVersions/$($Win32AppContentVersionRequest.id)/files/$($Win32AppFileContentRequest.id)" $ContentVersionsFiles = Wait-IntuneWin32AppFileProcessing -Stage "AzureStorageUriRequest" -Resource $FilesUri - + # Upload .intunewin file to Azure Storage blob - Invoke-AzureStorageBlobUpload -StorageUri $ContentVersionsFiles.azureStorageUri -FilePath $IntuneWinFilePath -Resource $FilesUri + if ($PSBoundParameters["UseAzCopy"]) { + $ContentSize = [System.Math]::Round($Win32AppFileBody.size / 1MB, 2) + if ($ContentSize -lt 100) { + Write-Verbose -Message "Content size is less than 100MB, falling back to using native method for file transfer" + Invoke-AzureStorageBlobUpload -StorageUri $ContentVersionsFiles.azureStorageUri -FilePath $IntuneWinFilePath -Resource $FilesUri + } + else { + try { + Write-Verbose -Message "Using AzCopy.exe method for file transfer" + $SplatArgs = @{ + StorageUri = $ContentVersionsFiles.azureStorageUri + FilePath = $IntuneWinFilePath + Resource = $FilesUri + WindowStyle = $AzCopyWindowStyle + ErrorAction = "Stop" + } + Invoke-AzureCopyUtility @SplatArgs + } + catch [System.Exception] { + Write-Verbose -Message "AzCopy.exe transfer method failed with exception message: $($_.Exception.Message)" + Write-Verbose -Message "Falling back to native method" + Invoke-AzureStorageBlobUpload -StorageUri $ContentVersionsFiles.azureStorageUri -FilePath $IntuneWinFilePath -Resource $FilesUri + } + } + } + else { + Write-Verbose -Message "Using native method for file transfer" + Invoke-AzureStorageBlobUpload -StorageUri $ContentVersionsFiles.azureStorageUri -FilePath $IntuneWinFilePath -Resource $FilesUri + } # Retrieve encryption meta data from .intunewin file $IntuneWinEncryptionInfo = [ordered]@{ - "encryptionKey" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.EncryptionKey - "macKey" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.macKey + "encryptionKey" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.EncryptionKey + "macKey" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.macKey "initializationVector" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.initializationVector - "mac" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.mac - "profileIdentifier" = "ProfileVersion1" - "fileDigest" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.fileDigest - "fileDigestAlgorithm" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.fileDigestAlgorithm + "mac" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.mac + "profileIdentifier" = "ProfileVersion1" + "fileDigest" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.fileDigest + "fileDigestAlgorithm" = $IntuneWinXMLMetaData.ApplicationInfo.EncryptionInfo.fileDigestAlgorithm } $IntuneWinFileEncryptionInfo = @{ "fileEncryptionInfo" = $IntuneWinEncryptionInfo @@ -140,13 +177,13 @@ function Update-IntuneWin32AppPackageFile { # Wait for Intune service to process the commit file request Write-Verbose -Message "Waiting for Intune service to process the commit file request" $CommitFileRequest = Wait-IntuneWin32AppFileProcessing -Stage "CommitFile" -Resource $FilesUri - + # Update committedContentVersion property for Win32 app Write-Verbose -Message "Updating committedContentVersion property with ID '$($Win32AppContentVersionRequest.id)' for Win32 app with ID: $($Win32App.id)" $Win32AppFileCommitBody = [ordered]@{ - "@odata.type" = "#microsoft.graph.win32LobApp" + "@odata.type" = "#microsoft.graph.win32LobApp" "committedContentVersion" = $Win32AppContentVersionRequest.id - "largeIcon" = $Win32App.largeIcon + "largeIcon" = $Win32App.largeIcon } $Win32AppFileCommitBodyRequest = Invoke-IntuneGraphRequest -APIVersion "Beta" -Resource "mobileApps/$($Win32App.id)" -Method "PATCH" -Body ($Win32AppFileCommitBody | ConvertTo-Json)