diff --git a/IntuneWin32App.psd1 b/IntuneWin32App.psd1 index 9f40469..be769cb 100644 --- a/IntuneWin32App.psd1 +++ b/IntuneWin32App.psd1 @@ -73,7 +73,8 @@ FunctionsToExport = @("Add-IntuneWin32App", "Add-IntuneWin32AppAssignmentAllUsers", "Add-IntuneWin32AppAssignmentGroup", "Add-IntuneWin32AppDependency", - "Add-IntuneWin32AppSupersedence" + "Add-IntuneWin32AppRequirement", + "Add-IntuneWin32AppSupersedence", "Connect-MSIntuneGraph", "Expand-IntuneWin32AppPackage", "Get-IntuneWin32App", @@ -82,6 +83,7 @@ FunctionsToExport = @("Add-IntuneWin32App", "Get-IntuneWin32AppDependency", "Get-IntuneWin32AppMetaData", "Get-IntuneWin32AppRelationship", + "Get-IntuneWin32AppRequirement", "Get-IntuneWin32AppSupersedence", "Get-MSIMetaData", "New-IntuneWin32AppDependency", @@ -103,6 +105,7 @@ FunctionsToExport = @("Add-IntuneWin32App", "Remove-IntuneWin32AppAssignmentAllUsers", "Remove-IntuneWin32AppAssignmentGroup", "Remove-IntuneWin32AppDependency", + "Remove-IntuneWin32AppRequirement", "Remove-IntuneWin32AppSupersedence", "Set-IntuneWin32App", "Test-AccessToken", diff --git a/Private/New-DelegatedAccessToken.ps1 b/Private/New-DelegatedAccessToken.ps1 index 4bc129d..d72be52 100644 --- a/Private/New-DelegatedAccessToken.ps1 +++ b/Private/New-DelegatedAccessToken.ps1 @@ -231,6 +231,9 @@ function New-DelegatedAccessToken { # Add AccessToken property for consistent access $TokenResponse | Add-Member -MemberType NoteProperty -Name "AccessToken" -Value $TokenResponse.access_token -Force + + # Store client_id for silent token renewal + $TokenResponse | Add-Member -MemberType NoteProperty -Name "client_id" -Value $ClientID -Force # Store refresh token if available for silent token renewal if ($TokenResponse.refresh_token) { diff --git a/Private/New-DeviceCodeAccessToken.ps1 b/Private/New-DeviceCodeAccessToken.ps1 index a961370..1a06e9f 100644 --- a/Private/New-DeviceCodeAccessToken.ps1 +++ b/Private/New-DeviceCodeAccessToken.ps1 @@ -140,6 +140,9 @@ function New-DeviceCodeAccessToken { # Add AccessToken property for consistent access $TokenResponse | Add-Member -MemberType NoteProperty -Name "AccessToken" -Value $TokenResponse.access_token -Force + + # Store client_id for silent token renewal + $TokenResponse | Add-Member -MemberType NoteProperty -Name "client_id" -Value $ClientID -Force # Store refresh token if available for silent token renewal if ($TokenResponse.refresh_token) { diff --git a/Private/Update-AccessTokenFromRefreshToken.ps1 b/Private/Update-AccessTokenFromRefreshToken.ps1 index 9352bb7..1581ae9 100644 --- a/Private/Update-AccessTokenFromRefreshToken.ps1 +++ b/Private/Update-AccessTokenFromRefreshToken.ps1 @@ -79,6 +79,9 @@ function Update-AccessTokenFromRefreshToken { # Add AccessToken property for consistent access $TokenResponse | Add-Member -MemberType NoteProperty -Name "AccessToken" -Value $TokenResponse.access_token -Force + + # Store client_id for subsequent silent token renewals + $TokenResponse | Add-Member -MemberType NoteProperty -Name "client_id" -Value $ClientID -Force # Store refresh token if available for subsequent silent token renewals if ($TokenResponse.refresh_token) { diff --git a/Public/Add-IntuneWin32AppRequirement.ps1 b/Public/Add-IntuneWin32AppRequirement.ps1 new file mode 100644 index 0000000..2949cdc --- /dev/null +++ b/Public/Add-IntuneWin32AppRequirement.ps1 @@ -0,0 +1,85 @@ +function Add-IntuneWin32AppRequirement { + <# + .SYNOPSIS + Add additional requirement rules to an existing Win32 application. + + .DESCRIPTION + Add additional requirement rules to an existing Win32 application. + Existing additional requirement rules on the app are preserved and the new rules + are appended to them. Use New-IntuneWin32AppRequirementRuleFile, + New-IntuneWin32AppRequirementRuleRegistry or New-IntuneWin32AppRequirementRuleScript + to construct the rule objects to pass to this function. + + .PARAMETER ID + Specify the ID for an existing Win32 application where additional requirement rules will be added. + + .PARAMETER AdditionalRequirementRule + Provide an array of a single or multiple OrderedDictionary objects created with + New-IntuneWin32AppRequirementRuleFile, New-IntuneWin32AppRequirementRuleRegistry or + New-IntuneWin32AppRequirementRuleScript functions. + + .NOTES + Author: Nickolaj Andersen + Contact: @NickolajA + Created: 2026-06-03 + Updated: 2026-06-03 + + Version history: + 1.0.0 - (2026-06-03) Function created + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param( + [parameter(Mandatory = $true, HelpMessage = "Specify the ID for an existing Win32 application where additional requirement rules will be added.")] + [ValidateNotNullOrEmpty()] + [string]$ID, + + [parameter(Mandatory = $true, HelpMessage = "Provide an array of a single or multiple OrderedDictionary objects created with New-IntuneWin32AppRequirementRuleFile, New-IntuneWin32AppRequirementRuleRegistry or New-IntuneWin32AppRequirementRuleScript functions.")] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.OrderedDictionary[]]$AdditionalRequirementRule + ) + Begin { + # Ensure required authentication header variable exists + if (-not (Test-AuthenticationState)) { + Write-Warning -Message "Authentication token was not found, use Connect-MSIntuneGraph before using this function"; break + } + + # Set script variable for error action preference + $ErrorActionPreference = "Stop" + } + Process { + # Retrieve Win32 app by ID from parameter input + Write-Verbose -Message "Querying for Win32 app using ID: $($ID)" + $Win32App = Invoke-MSGraphOperation -Get -APIVersion "Beta" -Resource "deviceAppManagement/mobileApps/$($ID)" + if ($null -ne $Win32App) { + $Win32AppID = $Win32App.id + + # Retrieve existing additional requirement rules and merge with new ones to avoid overwriting + $ExistingRequirementRules = Get-IntuneWin32AppRequirement -ID $Win32AppID + $MergedRequirementRules = if ($ExistingRequirementRules) { + @($ExistingRequirementRules) + @($AdditionalRequirementRule) + } + else { + @($AdditionalRequirementRule) + } + + # Construct request body for PATCH operation + $Win32AppBody = @{ + "@odata.type" = "#microsoft.graph.win32LobApp" + requirementRules = $MergedRequirementRules + } + + try { + # Attempt to call Graph and add additional requirement rules to Win32 app + Write-Verbose -Message "Attempting to add $($AdditionalRequirementRule.Count) additional requirement rule(s) to Win32 app with ID: $($Win32AppID)" + $Win32AppResponse = Invoke-MSGraphOperation -Patch -APIVersion "Beta" -Resource "deviceAppManagement/mobileApps/$($Win32AppID)" -Body ($Win32AppBody | ConvertTo-Json -Depth 10) -ContentType "application/json" + Write-Verbose -Message "Successfully added additional requirement rule(s) to Win32 app with ID: $($Win32AppID)" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while adding additional requirement rules to Win32 app: $($Win32AppID). Error message: $($_.Exception.Message)" + } + } + else { + Write-Warning -Message "Query for Win32 app returned an empty result, no apps matching the specified search criteria with ID '$($ID)' was found" + } + } +} diff --git a/Public/Get-IntuneWin32AppRequirement.ps1 b/Public/Get-IntuneWin32AppRequirement.ps1 new file mode 100644 index 0000000..9b2d0ff --- /dev/null +++ b/Public/Get-IntuneWin32AppRequirement.ps1 @@ -0,0 +1,58 @@ +function Get-IntuneWin32AppRequirement { + <# + .SYNOPSIS + Retrieve additional requirement rules from an existing Win32 application. + + .DESCRIPTION + Retrieve additional requirement rules from an existing Win32 application. + Returns the requirementRules array (file, registry or script based rules) as configured + on the app. Does not return the primary requirement rule (architecture, OS version, disk, memory). + + .PARAMETER ID + Specify the ID for an existing Win32 application to retrieve additional requirement rules from. + + .NOTES + Author: Nickolaj Andersen + Contact: @NickolajA + Created: 2026-06-03 + Updated: 2026-06-03 + + Version history: + 1.0.0 - (2026-06-03) Function created + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param( + [parameter(Mandatory = $true, HelpMessage = "Specify the ID for an existing Win32 application to retrieve additional requirement rules from.")] + [ValidateNotNullOrEmpty()] + [string]$ID + ) + Begin { + # Ensure required authentication header variable exists + if (-not (Test-AuthenticationState)) { + Write-Warning -Message "Authentication token was not found, use Connect-MSIntuneGraph before using this function"; break + } + + # Set script variable for error action preference + $ErrorActionPreference = "Stop" + } + Process { + # Retrieve Win32 app by ID from parameter input + Write-Verbose -Message "Querying for Win32 app using ID: $($ID)" + $Win32App = Invoke-MSGraphOperation -Get -APIVersion "Beta" -Resource "deviceAppManagement/mobileApps/$($ID)" + if ($null -ne $Win32App) { + # Handle return value + if ($null -ne $Win32App.requirementRules) { + return $Win32App.requirementRules + } + else { + Write-Verbose -Message "No additional requirement rules found for Win32 app with ID: $($ID)" + } + } + else { + Write-Warning -Message "Query for Win32 app returned an empty result, no apps matching the specified search criteria with ID '$($ID)' was found" + } + + # Return empty array for consistency + return @() + } +} diff --git a/Public/Remove-IntuneWin32AppRequirement.ps1 b/Public/Remove-IntuneWin32AppRequirement.ps1 new file mode 100644 index 0000000..ee6f328 --- /dev/null +++ b/Public/Remove-IntuneWin32AppRequirement.ps1 @@ -0,0 +1,104 @@ +function Remove-IntuneWin32AppRequirement { + <# + .SYNOPSIS + Remove additional requirement rules from an existing Win32 application. + + .DESCRIPTION + Remove additional requirement rules from an existing Win32 application. + Use the -All switch to remove all additional requirement rules, or filter by + rule type using the -RuleType parameter to remove only rules of a specific type. + + .PARAMETER ID + Specify the ID for an existing Win32 application where additional requirement rules will be removed. + + .PARAMETER All + Remove all additional requirement rules from the Win32 application. + + .PARAMETER RuleType + Remove only additional requirement rules of the specified type. + Supported values are: File, Registry, Script. + + .NOTES + Author: Nickolaj Andersen + Contact: @NickolajA + Created: 2026-06-03 + Updated: 2026-06-03 + + Version history: + 1.0.0 - (2026-06-03) Function created + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param( + [parameter(Mandatory = $true, HelpMessage = "Specify the ID for an existing Win32 application where additional requirement rules will be removed.")] + [ValidateNotNullOrEmpty()] + [string]$ID, + + [parameter(Mandatory = $true, ParameterSetName = "All", HelpMessage = "Remove all additional requirement rules from the Win32 application.")] + [switch]$All, + + [parameter(Mandatory = $true, ParameterSetName = "RuleType", HelpMessage = "Remove only additional requirement rules of the specified type. Supported values are: File, Registry, Script.")] + [ValidateSet("File", "Registry", "Script")] + [ValidateNotNullOrEmpty()] + [string]$RuleType + ) + Begin { + # Ensure required authentication header variable exists + if (-not (Test-AuthenticationState)) { + Write-Warning -Message "Authentication token was not found, use Connect-MSIntuneGraph before using this function"; break + } + + # Set script variable for error action preference + $ErrorActionPreference = "Stop" + + # Build the odata.type filter string for the selected rule type + $RuleTypeMap = @{ + "File" = "#microsoft.graph.win32LobAppFileSystemRequirement" + "Registry" = "#microsoft.graph.win32LobAppRegistryRequirement" + "Script" = "#microsoft.graph.win32LobAppPowerShellScriptRequirement" + } + } + Process { + # Retrieve Win32 app by ID from parameter input + Write-Verbose -Message "Querying for Win32 app using ID: $($ID)" + $Win32App = Invoke-MSGraphOperation -Get -APIVersion "Beta" -Resource "deviceAppManagement/mobileApps/$($ID)" + if ($null -ne $Win32App) { + $Win32AppID = $Win32App.id + + # Retrieve existing additional requirement rules + $ExistingRequirementRules = Get-IntuneWin32AppRequirement -ID $Win32AppID + if ($null -eq $ExistingRequirementRules) { + Write-Verbose -Message "No additional requirement rules found for Win32 app with ID: $($Win32AppID), nothing to remove" + return + } + + # Determine the filtered set of rules to keep + if ($PSCmdlet.ParameterSetName -eq "All") { + Write-Verbose -Message "Removing all additional requirement rules from Win32 app with ID: $($Win32AppID)" + $RemainingRequirementRules = @() + } + else { + $ODataType = $RuleTypeMap[$RuleType] + Write-Verbose -Message "Removing additional requirement rules of type '$($ODataType)' from Win32 app with ID: $($Win32AppID)" + $RemainingRequirementRules = @($ExistingRequirementRules | Where-Object { $_.'@odata.type' -ne $ODataType }) + } + + # Construct request body for PATCH operation + $Win32AppBody = @{ + "@odata.type" = "#microsoft.graph.win32LobApp" + requirementRules = $RemainingRequirementRules + } + + try { + # Attempt to call Graph and update requirement rules on Win32 app + $Win32AppResponse = Invoke-MSGraphOperation -Patch -APIVersion "Beta" -Resource "deviceAppManagement/mobileApps/$($Win32AppID)" -Body ($Win32AppBody | ConvertTo-Json -Depth 10) -ContentType "application/json" + Write-Verbose -Message "Successfully removed additional requirement rule(s) from Win32 app with ID: $($Win32AppID)" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while removing additional requirement rules from Win32 app: $($Win32AppID). Error message: $($_.Exception.Message)" + } + } + else { + Write-Warning -Message "Query for Win32 app returned an empty result, no apps matching the specified search criteria with ID '$($ID)' was found" + } + } +}