modules/Devolutions.CIEM.EffectivePermissions/Private/ResolveCIEMAzureEffectivePermission.ps1
|
function ResolveCIEMAzureEffectivePermission { [CmdletBinding()] [OutputType([CIEMEffectivePermission[]])] param( [Parameter()] [bool]$IncludeInherited = $true ) $ErrorActionPreference = 'Stop' function NewEvidence { param( [Parameter(Mandatory)] [string]$Id, [Parameter(Mandatory)] [string]$SourceApi, [Parameter(Mandatory)] [string]$SourceRecordId, [Parameter()] [AllowNull()] [string]$DataJson, [Parameter()] [AllowNull()] [string]$CollectedAt ) $ErrorActionPreference = 'Stop' $e = [CIEMEffectivePermissionEvidence]::new() $e.Id = $Id $e.SourceSystem = 'Azure' $e.SourceApi = $SourceApi $e.SourceRecordId = $SourceRecordId $e.CollectedAt = $CollectedAt $e.DataJson = $DataJson $e } function NewPrincipal { param([object]$Row) $ErrorActionPreference = 'Stop' $p = [CIEMEffectivePrincipal]::new() $p.Id = $Row.principal_id $p.DisplayName = $Row.principal_name $p.Type = ResolveCIEMPrincipalType -Kind $Row.principal_kind $p.NativeType = $Row.principal_kind $p.ProviderAccountId = $Row.principal_subscription_id $p.PropertiesJson = $Row.principal_properties $p } function NewTarget { param([object]$Row) $ErrorActionPreference = 'Stop' $region = $null if ($Row.target_properties) { $targetProperties = $Row.target_properties | ConvertFrom-Json -ErrorAction Stop $region = $targetProperties.location } $r = [CIEMEffectiveResource]::new() $r.Id = $Row.target_id $r.DisplayName = $Row.target_name $r.Type = $Row.target_kind $r.ProviderAccountId = $Row.target_subscription_id $r.Region = $region $r.PropertiesJson = $Row.target_properties $r } function NewPathStep { param( [Parameter(Mandatory)] [CIEMPermissionPathType]$Type, [Parameter()] [AllowNull()] [string]$SourceId, [Parameter()] [AllowNull()] [string]$SourceName, [Parameter()] [AllowNull()] [string]$TargetId, [Parameter()] [AllowNull()] [string]$TargetName, [Parameter(Mandatory)] [string]$Description, [Parameter(Mandatory)] [string]$EvidenceId ) $ErrorActionPreference = 'Stop' $s = [CIEMEffectivePermissionPathStep]::new() $s.Order = 1 $s.Type = $Type $s.SourceId = $SourceId $s.SourceName = $SourceName $s.TargetId = $TargetId $s.TargetName = $TargetName $s.Description = $Description $s.EvidenceId = $EvidenceId $s } $results = [System.Collections.Generic.List[object]]::new() $roleKinds = if ($IncludeInherited) { "'HasRole','InheritedRole'" } else { "'HasRole'" } $sql = @" SELECT e.id AS edge_id, e.kind AS edge_kind, e.properties AS edge_properties, e.collected_at AS edge_collected_at, p.id AS principal_id, p.kind AS principal_kind, p.display_name AS principal_name, p.subscription_id AS principal_subscription_id, p.properties AS principal_properties, t.id AS target_id, t.kind AS target_kind, t.display_name AS target_name, t.subscription_id AS target_subscription_id, t.properties AS target_properties FROM graph_edges e JOIN graph_nodes p ON p.id = e.source_id JOIN graph_nodes t ON t.id = e.target_id WHERE e.kind IN ($roleKinds) "@ foreach ($row in @(Invoke-CIEMQuery -Query $sql)) { $props = if ($row.edge_properties) { $row.edge_properties | ConvertFrom-Json -ErrorAction Stop } else { [pscustomobject]@{} } $evidenceId = "azure-graph-edge-$($row.edge_id)" $privileged = if ($null -ne $props.privileged) { [bool]$props.privileged } else { $false } $permission = [CIEMEffectivePermission]::new() $permission.Provider = [CIEMEffectivePermissionProvider]::Azure $permission.Principal = NewPrincipal -Row $row $target = NewTarget -Row $row $permission.Target = $target $permission.Privileged = $privileged $permission.CollectedAt = $row.edge_collected_at $entitlement = [CIEMEffectiveEntitlement]::new() $entitlement.Id = $props.role_definition_id $entitlement.Name = $props.role_name $entitlement.Type = [CIEMEntitlementType]::RoleAssignment $entitlement.NativeType = 'AzureRBAC' $entitlement.ScopeId = $row.target_id $entitlement.ScopeType = $row.target_kind $entitlement.PropertiesJson = $row.edge_properties $permission.Entitlement = $entitlement $permission.Actions = @(ConvertCIEMEffectivePermissionAction -Provider Azure -PermissionsJson $props.permissions_json -EntitlementName $props.role_name -Privileged:$privileged -TargetType $target.Type) if ($row.edge_kind -eq 'InheritedRole') { $permission.Path = @( NewPathStep -Type GroupInherited ` -SourceId $props.inherited_from ` -SourceName $props.inherited_from_name ` -TargetId $row.target_id ` -TargetName $row.target_name ` -Description "Inherited role assignment through group '$($props.inherited_from_name)'" ` -EvidenceId $evidenceId ) } else { $permission.Path = @( NewPathStep -Type Direct ` -SourceId $row.principal_id ` -SourceName $row.principal_name ` -TargetId $row.target_id ` -TargetName $row.target_name ` -Description 'Direct role assignment' ` -EvidenceId $evidenceId ) } $permission.Evidence = @( NewEvidence -Id $evidenceId -SourceApi 'graph_edges' -SourceRecordId ([string]$row.edge_id) -DataJson $row.edge_properties -CollectedAt $row.edge_collected_at ) $results.Add($permission) } $directorySql = @" SELECT e.id AS edge_id, e.kind AS edge_kind, e.properties AS edge_properties, e.collected_at AS edge_collected_at, p.id AS principal_id, p.kind AS principal_kind, p.display_name AS principal_name, p.subscription_id AS principal_subscription_id, p.properties AS principal_properties, t.id AS target_id, t.kind AS target_kind, t.display_name AS target_name, t.subscription_id AS target_subscription_id, t.properties AS target_properties FROM graph_edges e JOIN graph_nodes p ON p.id = e.source_id JOIN graph_nodes t ON t.id = e.target_id WHERE e.kind = 'HasRoleMember' "@ foreach ($row in @(Invoke-CIEMQuery -Query $directorySql)) { $evidenceId = "azure-graph-edge-$($row.edge_id)" $target = NewTarget -Row $row $actions = @(ConvertCIEMEffectivePermissionAction -Provider Azure -EntitlementName $row.target_name -NativeAction @($row.target_name) -TargetType $target.Type) $privileged = @($actions | Where-Object { $_.AccessLevel -eq [CIEMAccessLevel]::PermissionAdmin }).Count -gt 0 $permission = [CIEMEffectivePermission]::new() $permission.Provider = [CIEMEffectivePermissionProvider]::Azure $permission.Principal = NewPrincipal -Row $row $permission.Target = $target $permission.Actions = $actions $permission.Privileged = $privileged $permission.CollectedAt = $row.edge_collected_at $entitlement = [CIEMEffectiveEntitlement]::new() $entitlement.Id = $row.target_id $entitlement.Name = $row.target_name $entitlement.Type = [CIEMEntitlementType]::DirectoryRole $entitlement.NativeType = 'EntraDirectoryRole' $entitlement.ScopeId = $row.target_id $entitlement.ScopeType = $row.target_kind $entitlement.PropertiesJson = $row.target_properties $permission.Entitlement = $entitlement $permission.Path = @( NewPathStep -Type Direct -SourceId $row.principal_id -SourceName $row.principal_name -TargetId $row.target_id -TargetName $row.target_name -Description 'Directory role membership' -EvidenceId $evidenceId ) $permission.Evidence = @( NewEvidence -Id $evidenceId -SourceApi 'graph_edges' -SourceRecordId ([string]$row.edge_id) -DataJson $row.edge_properties -CollectedAt $row.edge_collected_at ) $results.Add($permission) } $consentSql = @" SELECT e.id AS edge_id, e.kind AS edge_kind, e.properties AS edge_properties, e.collected_at AS edge_collected_at, p.id AS principal_id, p.kind AS principal_kind, p.display_name AS principal_name, p.subscription_id AS principal_subscription_id, p.properties AS principal_properties, t.id AS target_id, t.kind AS target_kind, t.display_name AS target_name, t.subscription_id AS target_subscription_id, t.properties AS target_properties FROM graph_edges e JOIN graph_nodes p ON p.id = e.source_id JOIN graph_nodes t ON t.id = e.target_id WHERE e.kind IN ('HasAppRoleAssignment','HasOAuthGrant') "@ foreach ($row in @(Invoke-CIEMQuery -Query $consentSql)) { $props = if ($row.edge_properties) { $row.edge_properties | ConvertFrom-Json -ErrorAction Stop } else { [pscustomobject]@{} } $evidenceId = "azure-graph-edge-$($row.edge_id)" $isOAuth = $row.edge_kind -eq 'HasOAuthGrant' $nativeActions = if ($isOAuth -and $props.scope) { @($props.scope -split '\s+' | Where-Object { $_ }) } elseif ($props.app_role_id) { @($props.app_role_id) } else { @() } $permission = [CIEMEffectivePermission]::new() $permission.Provider = [CIEMEffectivePermissionProvider]::Azure $permission.Principal = NewPrincipal -Row $row $target = NewTarget -Row $row $permission.Target = $target $permission.Actions = @(ConvertCIEMEffectivePermissionAction -Provider Azure -EntitlementName $row.edge_kind -NativeAction $nativeActions -TargetType $target.Type) $permission.Privileged = @($permission.Actions | Where-Object { $_.AccessLevel -in @([CIEMAccessLevel]::Manage, [CIEMAccessLevel]::PermissionAdmin, [CIEMAccessLevel]::SecretAccess) }).Count -gt 0 $permission.CollectedAt = $row.edge_collected_at $entitlement = [CIEMEffectiveEntitlement]::new() $entitlement.Id = if ($isOAuth) { $props.grant_id } else { $props.assignment_id } $entitlement.Name = if ($isOAuth) { 'OAuth Grant' } else { 'App Role Assignment' } $entitlement.Type = if ($isOAuth) { [CIEMEntitlementType]::OAuthGrant } else { [CIEMEntitlementType]::AppRoleAssignment } $entitlement.NativeType = if ($isOAuth) { 'oauth2PermissionGrant' } else { 'appRoleAssignment' } $entitlement.ScopeId = $row.target_id $entitlement.ScopeType = $row.target_kind $entitlement.PropertiesJson = $row.edge_properties $permission.Entitlement = $entitlement $permission.Path = @( NewPathStep -Type $(if ($isOAuth) { [CIEMPermissionPathType]::OAuthGrant } else { [CIEMPermissionPathType]::AppRole }) ` -SourceId $row.principal_id ` -SourceName $row.principal_name ` -TargetId $row.target_id ` -TargetName $row.target_name ` -Description $entitlement.Name ` -EvidenceId $evidenceId ) $permission.Evidence = @( NewEvidence -Id $evidenceId -SourceApi 'graph_edges' -SourceRecordId ([string]$row.edge_id) -DataJson $row.edge_properties -CollectedAt $row.edge_collected_at ) $results.Add($permission) } @($results) } |