mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 10:55:38 +02:00
178 lines
6.2 KiB
PowerShell
178 lines
6.2 KiB
PowerShell
# Shared plumbing behind the policy-picker dialogs in both UI backends.
|
|
#
|
|
# The picker used to work off a full-tenant sweep: every Add Pair click ran
|
|
# Get-GraphPolicies once per policy group, which took minutes on a real tenant.
|
|
# Instead the user now picks a scope (one policy group or one policy type) and
|
|
# types a name; the search goes to Graph as a server-side prefix filter and
|
|
# comes back with just the matches.
|
|
#
|
|
# Lives in Internal/ rather than UI/ so WPF and Avalonia agree on the scope
|
|
# list and the search call, and so the UI stays a thin caller (R9/R10).
|
|
|
|
# Minimum characters before a search is sent. Below this the prefix matches
|
|
# most of the tenant, which is the slow full sweep this exists to avoid. Users
|
|
# who really want everything in the scope click "Load all in scope" instead.
|
|
$script:PolicySearchMinLength = 2
|
|
|
|
function Get-PolicySearchMinLength
|
|
{
|
|
return $script:PolicySearchMinLength
|
|
}
|
|
|
|
# The scope list for the picker's "Search in" dropdown: every policy group,
|
|
# each followed by its own policy types. Groups are listed first so picking
|
|
# "everything in Configuration" is one click; the indented type rows narrow it
|
|
# to a single API.
|
|
#
|
|
# Key is what the UI binds SelectedValue to - Id alone is not unique because a
|
|
# group and a type can carry the same Id (e.g. Applications).
|
|
function Get-PolicySearchScopes
|
|
{
|
|
param(
|
|
# Marks the scope for this policy type as the default selection. The
|
|
# compare flows pass the source policy's type, which is the scope the
|
|
# user wants almost every time.
|
|
[string]$DefaultPolicyTypeId
|
|
)
|
|
|
|
$scopes = [System.Collections.Generic.List[PSCustomObject]]::new()
|
|
|
|
foreach($group in @($script:IntuneGroups | Where-Object { $_.Title } | Sort-Object Title))
|
|
{
|
|
[void]$scopes.Add([PSCustomObject]@{
|
|
Key = "Group|$($group.Id)"
|
|
Id = $group.Id
|
|
Kind = "Group"
|
|
Title = "$($group.Title) (all types)"
|
|
GroupId = $group.Id
|
|
IsDefault = $false
|
|
})
|
|
|
|
foreach($type in @($group.PolicyTypes | Where-Object { $_.Title -and $_.API } | Sort-Object Title))
|
|
{
|
|
[void]$scopes.Add([PSCustomObject]@{
|
|
Key = "Type|$($type.Id)"
|
|
Id = $type.Id
|
|
Kind = "Type"
|
|
# Leading spaces indent the type under its group row. ASCII
|
|
# only - see the string-literal gate in Static.Tests.ps1.
|
|
Title = " $($type.Title)"
|
|
GroupId = $group.Id
|
|
IsDefault = ($DefaultPolicyTypeId -and $type.Id -eq $DefaultPolicyTypeId)
|
|
})
|
|
}
|
|
}
|
|
|
|
return $scopes.ToArray()
|
|
}
|
|
|
|
# The tenants the picker can search, one entry per live token - same source and
|
|
# labelling as the Copy dialog's destination-tenant combo (Get-TokenInfo, which
|
|
# is provider-agnostic, so MSAL / OAuth / MgGraph tokens all appear). Returns an
|
|
# empty array when only one tenant is signed in; callers hide the combo then.
|
|
#
|
|
# Searching another tenant works because Get-GraphPolicies stamps each policy
|
|
# with the TokenId it came from, and GetFullObject / Invoke-PolicyHydrate resolve
|
|
# against that stamp - so a policy picked from tenant B still hydrates and
|
|
# compares against tenant B.
|
|
function Get-PolicySearchTenants
|
|
{
|
|
$tokens = @(Get-TokenInfo)
|
|
if($tokens.Count -le 1) { return @() }
|
|
|
|
$tenants = [System.Collections.Generic.List[PSCustomObject]]::new()
|
|
foreach($token in $tokens)
|
|
{
|
|
if(-not $token) { continue }
|
|
|
|
$title = Get-StringOrDefault $token.TenantName $token.TenantID
|
|
if([String]::IsNullOrWhiteSpace($title)) { $title = "Tenant $($token.Id)" }
|
|
if($token.IsDefault -eq $true) { $title = "$title (Current)" }
|
|
|
|
[void]$tenants.Add([PSCustomObject]@{
|
|
Key = "Token|$($token.Id)"
|
|
TokenId = [int]$token.Id
|
|
TenantId = $token.TenantID
|
|
Title = $title
|
|
IsDefault = ($token.IsDefault -eq $true)
|
|
})
|
|
}
|
|
|
|
return $tenants.ToArray()
|
|
}
|
|
|
|
# Key of the tenant to preselect: the current one, else the first.
|
|
function Get-PolicySearchDefaultTenantKey
|
|
{
|
|
param($Tenants)
|
|
|
|
$all = @($Tenants)
|
|
if($all.Count -eq 0) { return $null }
|
|
|
|
$current = $all | Where-Object { $_.IsDefault } | Select-Object -First 1
|
|
if($current) { return $current.Key }
|
|
|
|
return $all[0].Key
|
|
}
|
|
|
|
# Default scope key for a policy type id: the type's own row when it is in the
|
|
# list, otherwise its group, otherwise the first scope. Returns $null when
|
|
# there are no scopes at all.
|
|
function Get-PolicySearchDefaultScopeKey
|
|
{
|
|
param(
|
|
$Scopes,
|
|
[string]$PolicyTypeId
|
|
)
|
|
|
|
$all = @($Scopes)
|
|
if($all.Count -eq 0) { return $null }
|
|
|
|
if($PolicyTypeId)
|
|
{
|
|
$typeScope = $all | Where-Object { $_.Kind -eq "Type" -and $_.Id -eq $PolicyTypeId } | Select-Object -First 1
|
|
if($typeScope) { return $typeScope.Key }
|
|
}
|
|
|
|
return $all[0].Key
|
|
}
|
|
|
|
# Run one scoped search. $Scope is an entry from Get-PolicySearchScopes; an
|
|
# empty $SearchText returns everything in the scope (the "Load all in scope"
|
|
# path). Types whose endpoint rejects a server-side filter are handled inside
|
|
# Get-GraphPolicies, which always re-checks names client-side.
|
|
function Search-IntunePolicies
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
$Scope,
|
|
[string]$SearchText,
|
|
# Ids to drop from the result - used to keep a policy from being
|
|
# compared against itself.
|
|
[string[]]$ExcludeIds,
|
|
[Int]$TokenId = (Get-DefaultTokenId)
|
|
)
|
|
|
|
if(-not $Scope) { return @() }
|
|
|
|
$params = @{ TokenId = $TokenId }
|
|
if($Scope.Kind -eq "Group") { $params.Add("PolicyGroup", $Scope.Id) }
|
|
else { $params.Add("PolicyType", $Scope.Id) }
|
|
if($SearchText) { $params.Add("NameFilter", $SearchText) }
|
|
|
|
$found = @()
|
|
try {
|
|
$found = @(Get-GraphPolicies @params)
|
|
}
|
|
catch {
|
|
Write-LogError "Search-IntunePolicies failed for scope $($Scope.Key)" $_.Exception
|
|
return @()
|
|
}
|
|
|
|
if($ExcludeIds -and $ExcludeIds.Count -gt 0) {
|
|
$found = @($found | Where-Object { $ExcludeIds -notcontains $_.Id })
|
|
}
|
|
|
|
return $found
|
|
}
|