Public/Setup/Get-TBPermissionPlan.ps1
|
function Get-TBPermissionPlan { <# .SYNOPSIS Builds a permission plan for UTCM workloads/resource types. .DESCRIPTION Returns graph permissions that can be auto-granted and manual remediation steps needed for providers where automatic assignment is not supported. .PARAMETER Workload One or more workload names. .PARAMETER ResourceType One or more resource types. Aliases are auto-resolved unless invalid. .EXAMPLE Get-TBPermissionPlan -Workload MultiWorkload .EXAMPLE Get-TBPermissionPlan -ResourceType microsoft.entra.conditionalaccesspolicy #> [CmdletBinding(DefaultParameterSetName = 'ByWorkload')] [OutputType([PSCustomObject])] param( [Parameter(Mandatory = $true, ParameterSetName = 'ByWorkload')] [ValidateSet('ConditionalAccess', 'EntraID', 'ExchangeOnline', 'Intune', 'Teams', 'SecurityAndCompliance', 'MultiWorkload')] [string[]]$Workload, [Parameter(Mandatory = $true, ParameterSetName = 'ByResourceType')] [string[]]$ResourceType ) $catalog = Get-TBUTCMCatalog $profileLookup = @{} foreach ($profileEntry in $catalog.PermissionProfiles.PSObject.Properties) { $profileLookup[$profileEntry.Name] = $profileEntry.Value } $selectedWorkloads = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $resolvedResourceTypes = [System.Collections.ArrayList]::new() $useResourceLevelPerms = $false $resourceLevelPerms = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $fallbackWorkloads = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) if ($PSCmdlet.ParameterSetName -eq 'ByWorkload') { foreach ($item in $Workload) { if ($item -eq 'MultiWorkload') { foreach ($name in $profileLookup.Keys) { $null = $selectedWorkloads.Add($name) } } else { $null = $selectedWorkloads.Add($item) } } } else { $useResourceLevelPerms = $true $resourceLookup = @{} foreach ($resource in $catalog.Resources) { $resourceLookup[$resource.Name.ToLowerInvariant()] = $resource } $warningTracker = @{} foreach ($item in $ResourceType) { $resolved = Resolve-TBResourceType -ResourceType $item -WarningTracker $warningTracker $null = $resolvedResourceTypes.Add($resolved.CanonicalResourceType) $resource = $resourceLookup[$resolved.CanonicalResourceType] if (-not $resource) { continue } # Check for per-resource GraphReadPermissions first $hasResourcePerms = $false if ($resource.PSObject.Properties['GraphReadPermissions'] -and $resource.GraphReadPermissions.Count -gt 0) { foreach ($perm in $resource.GraphReadPermissions) { $null = $resourceLevelPerms.Add($perm) } $hasResourcePerms = $true } # Determine the workload for fallback or manual steps $workloadName = $null if ($resolved.CanonicalResourceType -eq 'microsoft.entra.conditionalaccesspolicy') { $workloadName = 'ConditionalAccess' } elseif ($resource.WorkloadId) { $workloadName = $resource.WorkloadId } if ($workloadName) { $null = $selectedWorkloads.Add($workloadName) if (-not $hasResourcePerms) { # No per-resource permissions; fall back to full workload profile $null = $fallbackWorkloads.Add($workloadName) } } } } $autoGrant = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $manualSteps = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) if ($useResourceLevelPerms -and $resourceLevelPerms.Count -gt 0) { # Add resource-level permissions foreach ($perm in $resourceLevelPerms) { $null = $autoGrant.Add($perm) } # Add workload profile permissions only for resources without per-resource data foreach ($name in $fallbackWorkloads) { $profileDetails = $profileLookup[$name] if (-not $profileDetails) { continue } foreach ($permission in @($profileDetails.AutoGrantGraphPermissions)) { if ($permission) { $null = $autoGrant.Add($permission) } } } # Always include manual steps from all relevant workloads foreach ($name in $selectedWorkloads) { $profileDetails = $profileLookup[$name] if (-not $profileDetails) { continue } foreach ($step in @($profileDetails.ManualSteps)) { if ($step) { $null = $manualSteps.Add($step) } } } } else { foreach ($name in $selectedWorkloads) { $profileDetails = $profileLookup[$name] if (-not $profileDetails) { continue } foreach ($permission in @($profileDetails.AutoGrantGraphPermissions)) { if ($permission) { $null = $autoGrant.Add($permission) } } foreach ($step in @($profileDetails.ManualSteps)) { if ($step) { $null = $manualSteps.Add($step) } } } } [PSCustomObject]@{ PSTypeName = 'TenantBaseline.PermissionPlan' GeneratedAt = (Get-Date).ToString('o') CatalogSource = $catalog.Source CatalogSchemaVersion = $catalog.SchemaVersion RequestedWorkloads = @($selectedWorkloads | Sort-Object) RequestedResourceTypes = if ($ResourceType) { @($ResourceType) } else { @() } CanonicalResourceTypes = @($resolvedResourceTypes | Sort-Object -Unique) AutoGrantGraphPermissions = @($autoGrant | Sort-Object) ManualSteps = @($manualSteps | Sort-Object) } } |