pwsh/dev/functions/getPIMEligible.ps1 (202 lines of code) (raw):
function getPIMEligible {
$start = Get-Date
$currentTask = 'Get PIM onboarded Subscriptions and Management Groups'
Write-Host $currentTask
$uriExt = "&`$expand=parent&`$filter=(type eq 'subscription' or type eq 'managementgroup')"
$uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/privilegedAccess/azureResources/resources?`$select=id,displayName,type,externalId" + $uriExt
$res = AzAPICall -AzAPICallConfiguration $azapicallConf -uri $uri -currentTask $currentTask
if ($res.Count -gt 0) {
$scopesToIterate = [System.Collections.ArrayList]@()
if (-not $PIMEligibilityIgnoreScope) {
if (($azAPICallConf['checkContext']).Tenant.Id -ne $ManagementGroupId) {
foreach ($entry in $res) {
$entryIdGuid = $entry.externalId -replace '.*/'
if ($entry.type -eq 'managementGroup') {
if ($htManagementGroupsMgPath.($ManagementGroupId).ParentNameChain -contains ($entryIdGuid) -or $htManagementGroupsMgPath.($entryIdGuid).path -contains $ManagementGroupId) {
$null = $scopesToIterate.Add($entry)
}
}
if ($entry.type -eq 'subscription') {
if ($htSubscriptionsMgPath.($entryIdGuid).ParentNameChain -contains $ManagementGroupId) {
if ($htOutOfScopeSubscriptions.($entryIdGuid)) {
Write-Host "excluding subscription $($entryIdGuid) (outOfScopeSubscription -> $($htOutOfScopeSubscriptions.($entryIdGuid).outOfScopeReason)) (`$PIMEligibilityIgnoreScope=$PIMEligibilityIgnoreScope)"
}
else {
$null = $scopesToIterate.Add($entry)
}
}
}
}
}
else {
foreach ($entry in $res) {
$entryIdGuid = $entry.externalId -replace '.*/'
if ($htOutOfScopeSubscriptions.($entryIdGuid)) {
Write-Host "excluding subscription $($entryIdGuid) (outOfScopeSubscription -> $($htOutOfScopeSubscriptions.($entryIdGuid).outOfScopeReason)) (`$PIMEligibilityIgnoreScope=$PIMEligibilityIgnoreScope)"
}
else {
$null = $scopesToIterate.Add($entry)
}
}
}
}
else {
foreach ($entry in $res) {
$null = $scopesToIterate.Add($entry)
}
}
$PIMOnboardedGrouped = $scopesToIterate | Group-Object -Property type
foreach ($entry in $PIMOnboardedGrouped) {
Write-Host " Found $($entry.Count) PIM onboarded $($entry.Name)s"
}
$htPIMEligibleDirect = [System.Collections.Hashtable]::Synchronized(@{})
$relevantSubscriptionIds = $subsToProcessInCustomDataCollection.subscriptionId
if ($scopesToIterate.Count -gt 0) {
$batchSize = [math]::ceiling($scopesToIterate.Count / $ThrottleLimit)
Write-Host "Optimal batch size: $($batchSize)"
$counterBatch = [PSCustomObject] @{ Value = 0 }
$scopesToIterateBatch = ($scopesToIterate) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
Write-Host "Processing data in $($scopesToIterateBatch.Count) batches"
$scopesToIterateBatch | ForEach-Object -Parallel {
$scope = $_
$azAPICallConf = $using:azAPICallConf
$arrayPIMEligible = $using:arrayPIMEligible
$htPIMEligibleDirect = $using:htPIMEligibleDirect
$htPrincipals = $using:htPrincipals
$htUserTypesGuest = $using:htUserTypesGuest
$htServicePrincipals = $using:htServicePrincipals
$relevantSubscriptionIds = $using:relevantSubscriptionIds
$function:resolveObjectIds = $using:funcResolveObjectIds
$function:testGuid = $using:funcTestGuid
foreach ($scope in $_.Group) {
if ($scope.type -eq 'managementgroup') { $htManagementGroupsMgPath = $using:htManagementGroupsMgPath }
if ($scope.type -eq 'subscription') { $htSubscriptionsMgPath = $using:htSubscriptionsMgPath }
$processThisScope = $true
if ($scope.type -eq 'subscription') {
if (($scope.externalId -replace '.*/') -notin $relevantSubscriptionIds) {
Write-Host " Non relevant subscriptionId '$(($scope.externalId -replace '.*/'))' /skipping this subscription as it is not contained in the 'Relevant Subscriptions' collection (needs investigation)" -ForegroundColor DarkRed
$processThisScope = $false
}
}
if ($processThisScope -eq $true) {
$currentTask = "Get Eligible assignments for Scope $($scope.type): $($scope.externalId -replace '.*/')"
$extUri = "?`$expand=linkedEligibleRoleAssignment,subject,roleDefinition(`$expand=resource)&`$count=true&`$filter=(roleDefinition/resource/id eq '$($scope.id)')+and+(assignmentState eq 'Eligible')&`$top=100"
$uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/privilegedAccess/azureResources/roleAssignments" + $extUri
$resx = AzAPICall -AzAPICallConfiguration $azapicallConf -currentTask $currentTask -uri $uri
if ($resx.Count -gt 0) {
$users = $resx.where({ $_.subject.type -eq 'user' })
if ($users.Count -gt 0) {
ResolveObjectIds -objectIds $users.subject.id -showActivity
}
foreach ($entry in $resx) {
$scopeId = $scope.externalId -replace '.*/'
if ($scope.type -eq 'managementgroup') {
$ScopeType = 'MG'
$ManagementGroupId = $scopeId
$SubscriptionId = ''
$SubscriptionDisplayName = ''
if ($htManagementGroupsMgPath.($scopeId)) {
$MgDetails = $htManagementGroupsMgPath.($scopeId)
$ManagementGroupDisplayName = $MgDetails.DisplayName
$ScopeDisplayName = $MgDetails.DisplayName
$MgPath = $MgDetails.path
$MgLevel = $MgDetails.level
}
else {
$ManagementGroupDisplayName = 'notAccessible'
$ScopeDisplayName = 'notAccessible'
$MgPath = 'notAccessible'
$MgLevel = 'notAccessible'
}
if ($entry.memberType -eq 'direct') {
$script:htPIMEligibleDirect.($entry.id) = @{}
$script:htPIMEligibleDirect.($entry.id).clear = $scopeId
if ($scopeId -eq $ManagementGroupDisplayName) {
$script:htPIMEligibleDirect.($entry.id).enriched = "$($scopeId) [Level $($MgLevel)]"
}
else {
$script:htPIMEligibleDirect.($entry.id).enriched = "$($ManagementGroupDisplayName) ($($scopeId)) [Level $($MgLevel)]"
}
}
}
if ($scope.type -eq 'subscription') {
$ScopeType = 'Sub'
#$ManagementGroupId = ''
$SubscriptionId = $scopeId
if ($htSubscriptionsMgPath.($scopeId)) {
$MgDetails = $htSubscriptionsMgPath.($scopeId)
$SubscriptionDisplayName = $MgDetails.DisplayName
$ScopeDisplayName = $MgDetails.DisplayName
$MgPath = $MgDetails.path
$MgLevel = $MgDetails.level
$ManagementGroupId = $MgDetails.Parent
$ManagementGroupDisplayName = $MgDetails.ParentName
}
else {
$SubscriptionDisplayName = 'notAccessible'
$ScopeDisplayName = 'notAccessible'
$MgPath = 'notAccessible'
$MgLevel = 'notAccessible'
}
#$ManagementGroupDisplayName = ''
}
if ($entry.subject.type -eq 'user') {
if ($htPrincipals.($entry.subject.id)) {
$userDetail = $htPrincipals.($entry.subject.id)
$principalType = "$($userDetail.type) $($userDetail.userType)"
}
else {
$principalType = $entry.subject.type
}
}
else {
$principalType = $entry.subject.type
}
$roleType = 'undefined'
if ($entry.roleDefinition.type -eq 'BuiltInRole') { $roleType = 'Builtin' }
if ($entry.roleDefinition.type -eq 'CustomRole') { $roleType = 'Custom' }
$null = $script:arrayPIMEligible.Add([PSCustomObject]@{
ScopeType = $ScopeType
ScopeId = $scopeId
ScopeDisplayName = $ScopeDisplayName
ManagementGroupId = $ManagementGroupId
ManagementGroupDisplayName = $ManagementGroupDisplayName
SubscriptionId = $SubscriptionId
SubscriptionDisplayName = $SubscriptionDisplayName
MgPath = $MgPath
MgLevel = $MgLevel
RoleId = $entry.roleDefinition.externalId
RoleIdGuid = $entry.roleDefinition.externalId -replace '.*/'
RoleType = $roleType
RoleName = $entry.roleDefinition.displayName
IdentityObjectId = $entry.subject.id
IdentityType = $principalType
IdentityDisplayName = $entry.subject.displayName
IdentityPrincipalName = $entry.subject.principalName
PIMId = $entry.id
PIMInheritance = $entry.memberType
PIMInheritedFromClear = ''
PIMInheritedFrom = ''
PIMStartDateTime = $entry.startDateTime
PIMEndDateTime = $entry.endDateTime
})
}
}
}
}
} -ThrottleLimit $ThrottleLimit
}
foreach ($entry in $arrayPIMEligible) {
if ($entry.PIMInheritance -eq 'inherited') {
$entry.PIMInheritedFromClear = $htPIMEligibleDirect.($entry.PIMId).clear
$entry.PIMInheritedFrom = $htPIMEligibleDirect.($entry.PIMId).enriched
}
}
$script:arrayPIMEligibleGrouped = $arrayPIMEligible | Group-Object -Property ScopeType
foreach ($entry in $arrayPIMEligibleGrouped) {
Write-Host " Found $($entry.Count) PIM Eligible assignments for $($entry.Name)s"
}
}
$end = Get-Date
Write-Host "Getting PIM Eligible assignments processing duration: $((New-TimeSpan -Start $start -End $end).TotalMinutes) minutes ($((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds)"
}