Get-AzureAssessPrivilegedRoleAssignments.ps1

<#
.SYNOPSIS
    Get Privilege role assignement for specific Id (resource or container)
.EXAMPLE
    PS C:\Get-AzureAssessPrivilegedRoleAssginments -Id /subscriptions/151f94b9-9d98-45cd-bc0b-d3bdee8d2969
#>


$privilgedRoles = @{}
$managementGroupNames = @{}
$subscriptionNames = @{}

function Get-AzureAssessPrivilegedRoleAssignments {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
        [string]$SubscriptionId,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 2)]
        [string]$ResourceGroupName
    )
 
    # get context
    $context = get-azcontext

    # Get assignement for privileged roles: Owner, Contributor, User Access Administrator, Role Bases Access Control Administrator
    if ($script:privilgedRoles.Count -eq 0) {
        $res = invoke-azrestmethod -method "GET" -path "/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-04-01"
        if ($res.StatusCode -eq 200) {
            $res.Content `
                | ConvertFrom-Json | Select-Object -ExpandProperty value `
                | Where-Object { $_.properties.roleName -in ("Owner","Contributor","User Access Administrator","Role Based Access Control Administrator")}
                | foreach-Object { $script:privilgedRoles[$_.name] = $_.properties.roleName }
        } 
    }

    # get the role assignements for the resource group
    $res = invoke-azrestmethod -method "GET" -path "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01"
    if ($res.StatusCode -ne 200) {
        return
    }
    # returned columns
    # roleId,role,principalId,principalType,scope,source,resourceType,resourceName
    $assignments = @(,$res.Content | ConvertFrom-Json | Select-Object -ExpandProperty value | Select-Object -ExpandProperty properties `
        | Where-Object { ($_.roleDefinitionId -split "/")[-1] -in $privilgedRoles.Keys } `
        | Select-Object `
            @{N="roleId";E={($_.roleDefinitionId -split "/")[-1]}}, `
            @{N="role";E={""}}, `
            principalId, `
            principalType, `
            scope, `
            @{N="source";E={"RBAC"}}, `
            @{N="resourceType";E={""}}, `
            @{N="resourceName";E={($_.scope -split "/")[-1]}}, `
            @{N="link";E={"https://portal.azure.com/#@$($context.Tenant.Id)/resource$($_.scope)/users"}})
    # expand columns
    foreach($assignment in $assignments) {
        $assignment.role = $script:privilgedRoles[$assignment.roleId]
        if ($assignment.scope -eq "/") {
            $assignment.resourceType = "managementGroups"
            $assignment.resourceName = "root"
            if ($assignment.resourceName -notin $script:managementGroupNames.Keys) {

                $res = Invoke-AzRestMethod -Method GET -Path "/providers/Microsoft.Management/managementGroups/$($context.Tenant.Id)?api-version=2020-05-01"
                if ($res.StatusCode -eq 200) {
                    $script:managementGroupNames[$assignment.resourceName] =  ($res.Content | ConvertFrom-Json).properties.displayName
                }
            } 
            if ($assignment.resourceName -in $script:managementGroupNames.Keys) {
                $assignment.scope = $assignment.scope -replace $assignment.resourceName,$script:managementGroupNames[$assignment.resourceName]
                $assignment.link = "https://portal.azure.com/#@$($context.Tenant.Id)/resource/providers/Microsoft.Management/managementGroups/$($context.Tenant.Id)/users"
                $assignment.resourceName = $script:managementGroupNames[$assignment.resourceName]
            }
        } elseif ($assignment.scope -imatch "/providers/Microsoft.Management/managementGroups/[^/]+") {
            $assignment.resourceType = "managementGroups"
            if ($assignment.resourceName -notin $script:managementGroupNames.Keys) {
                $res = Invoke-AzRestMethod -Method GET -Path "/providers/Microsoft.Management/managementGroups/$($assignment.resourceName)?api-version=2020-05-01"
                if ($res.StatusCode -eq 200) {
                    $script:managementGroupNames[$assignment.resourceName] =  ($res.Content | ConvertFrom-Json).properties.displayName
                }
            } 
            if ($assignment.resourceName -in $script:managementGroupNames.Keys) {
                $assignment.scope = $assignment.scope -replace $assignment.resourceName,$script:managementGroupNames[$assignment.resourceName]
                $assignment.link = $assignment.link -replace $assignment.resourceName,$script:managementGroupNames[$assignment.resourceName]
                $assignment.resourceName = $script:managementGroupNames[$assignment.resourceName]
            }
        } elseif ($assignment.scope -imatch "/subscriptions/[^/]+/resourceGroups/[^/]+/providers/.+") {
            $assignment.resourceType = ($assignment.scope -split "/")[6..7] -join "/"
        } elseif ($assignment.scope -imatch "/subscriptions/[^/]+/resourceGroups/[^/]+") {
            $assignment.resourceType = "resourceGroups"
        } elseif ($assignment.scope -imatch "/subscriptions/[^/]+") {
            $assignment.resourceType = "subscriptions"
            if ($assignment.resourceName -notin $script:subscriptionNames.Keys) {

                $res = Invoke-AzRestMethod -Method GET -Path "/subscriptions/$($assignment.resourceName)?api-version=2016-06-01"
                if ($res.StatusCode -eq 200) {
                    $script:subscriptionNames[$assignment.resourceName] =  ($res.Content | ConvertFrom-Json).displayName
                }
            } 
            if ($assignment.resourceName -in $script:subscriptionNames.Keys) {
                $assignment.resourceName = $script:subscriptionNames[$assignment.resourceName]
            }
        }
    }
    # add inherited roles at resourceGroup level
    $inherits = $assignments | Where-Object { $_.resourceType -iin ("subscriptions","managementGroups")}
    foreach($inherit in $inherits) {
        $copy = $inherit.PsObject.Copy()
        $copy.scope = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName"
        $copy.resourceType = "resourceGroups"
        $copy.resourceName = $ResourceGroupName
        $copy.source = "Inherit"
        $copy.link = "https://portal.azure.com/#@$($context.Tenant.Id)/resource$($copy.scope)/users"
        $assignments += $copy
    }
    # add passing down roles to resources
    # get all resources of resourcegroup
    $res = invoke-azrestmethod -method GET -path "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/resources?api-version=2021-04-11"
    if ($res.StatusCode -ne 200) {
        return $assignments
    }
    $resources = $res.Content | ConvertFrom-Json | Select-Object -ExpandProperty value | Select-Object id,name,type
    $childassignments = $assignments | Where-Object { $_.resourceType -iin ("subscriptions","managementGroups","resourceGroups") -and $_.source -eq "RBAC"}
    foreach($childassignement in $childassignments) {
        foreach($resource in $resources) {
            $copy = $childassignement.PsObject.Copy()
            $copy.scope = $resource.id
            $copy.resourceType = $resource.type
            $copy.resourceName = $resource.name
            $copy.source = "Inherit"
            $copy.link = "https://portal.azure.com/#@$($context.Tenant.Id)/resource$($copy.scope)/users"
            $assignments += $copy
        }
    }
    $assignments
}
# SIG # Begin signature block
# MIIRWAYJKoZIhvcNAQcCoIIRSTCCEUUCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDsmQl6OT4cAx3u
# wt2QEhXGY3IRQABXNb4lKvkACmaczaCCDZIwgga5MIIEoaADAgECAhEAmaOACiZV
# O2Wr3G6EprPqOTANBgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNV
# BAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBD
# ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQg
# TmV0d29yayBDQSAyMB4XDTIxMDUxOTA1MzIxOFoXDTM2MDUxODA1MzIxOFowVjEL
# MAkGA1UEBhMCUEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEk
# MCIGA1UEAxMbQ2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAnSPPBDAjO8FGLOczcz5jXXp1ur5cTbq96y34
# vuTmflN4mSAfgLKTvggv24/rWiVGzGxT9YEASVMw1Aj8ewTS4IndU8s7VS5+djSo
# McbvIKck6+hI1shsylP4JyLvmxwLHtSworV9wmjhNd627h27a8RdrT1PH9ud0IF+
# njvMk2xqbNTIPsnWtw3E7DmDoUmDQiYi/ucJ42fcHqBkbbxYDB7SYOouu9Tj1yHI
# ohzuC8KNqfcYf7Z4/iZgkBJ+UFNDcc6zokZ2uJIxWgPWXMEmhu1gMXgv8aGUsRda
# CtVD2bSlbfsq7BiqljjaCun+RJgTgFRCtsuAEw0pG9+FA+yQN9n/kZtMLK+Wo837
# Q4QOZgYqVWQ4x6cM7/G0yswg1ElLlJj6NYKLw9EcBXE7TF3HybZtYvj9lDV2nT8m
# FSkcSkAExzd4prHwYjUXTeZIlVXqj+eaYqoMTpMrfh5MCAOIG5knN4Q/JHuurfTI
# 5XDYO962WZayx7ACFf5ydJpoEowSP07YaBiQ8nXpDkNrUA9g7qf/rCkKbWpQ5bou
# fUnq1UiYPIAHlezf4muJqxqIns/kqld6JVX8cixbd6PzkDpwZo4SlADaCi2JSplK
# ShBSND36E/ENVv8urPS0yOnpG4tIoBGxVCARPCg1BnyMJ4rBJAcOSnAWd18Jx5n8
# 58JSqPECAwEAAaOCAVUwggFRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFN10
# XUwA23ufoHTKsW73PMAywHDNMB8GA1UdIwQYMBaAFLahVDkCw6A/joq8+tT4HKbR
# Og79MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzAwBgNVHR8E
# KTAnMCWgI6Ahhh9odHRwOi8vY3JsLmNlcnR1bS5wbC9jdG5jYTIuY3JsMGwGCCsG
# AQUFBwEBBGAwXjAoBggrBgEFBQcwAYYcaHR0cDovL3N1YmNhLm9jc3AtY2VydHVt
# LmNvbTAyBggrBgEFBQcwAoYmaHR0cDovL3JlcG9zaXRvcnkuY2VydHVtLnBsL2N0
# bmNhMi5jZXIwOQYDVR0gBDIwMDAuBgRVHSAAMCYwJAYIKwYBBQUHAgEWGGh0dHA6
# Ly93d3cuY2VydHVtLnBsL0NQUzANBgkqhkiG9w0BAQwFAAOCAgEAdYhYD+WPUCia
# U58Q7EP89DttyZqGYn2XRDhJkL6P+/T0IPZyxfxiXumYlARMgwRzLRUStJl490L9
# 4C9LGF3vjzzH8Jq3iR74BRlkO18J3zIdmCKQa5LyZ48IfICJTZVJeChDUyuQy6rG
# DxLUUAsO0eqeLNhLVsgw6/zOfImNlARKn1FP7o0fTbj8ipNGxHBIutiRsWrhWM2f
# 8pXdd3x2mbJCKKtl2s42g9KUJHEIiLni9ByoqIUul4GblLQigO0ugh7bWRLDm0Cd
# Y9rNLqyA3ahe8WlxVWkxyrQLjH8ItI17RdySaYayX3PhRSC4Am1/7mATwZWwSD+B
# 7eMcZNhpn8zJ+6MTyE6YoEBSRVrs0zFFIHUR08Wk0ikSf+lIe5Iv6RY3/bFAEloM
# U+vUBfSouCReZwSLo8WdrDlPXtR0gicDnytO7eZ5827NS2x7gCBibESYkOh1/w1t
# VxTpV2Na3PR7nxYVlPu1JPoRZCbH86gc96UTvuWiOruWmyOEMLOGGniR+x+zPF/2
# DaGgK2W1eEJfo2qyrBNPvF7wuAyQfiFXLwvWHamoYtPZo0LHuH8X3n9C+xN4YaNj
# t2ywzOr+tKyEVAotnyU9vyEVOaIYMk3IeBrmFnn0gbKeTTyYeEEUz/Qwt4HOUBCr
# W602NCmvO1nm+/80nLy5r0AZvCQxaQ4wggbRMIIEuaADAgECAhBNn7NsFkgeJAAl
# SKxTsSjWMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhB
# c3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNp
# Z25pbmcgMjAyMSBDQTAeFw0yNDAxMTYxMjAwMzRaFw0yNTAxMTUxMjAwMzNaMHcx
# CzAJBgNVBAYTAkJFMRgwFgYDVQQIDA9XYWxsb29uIEJyYWJhbnQxHjAcBgNVBAoM
# FU9wZW4gU291cmNlIERldmVsb3BlcjEuMCwGA1UEAwwlT3BlbiBTb3VyY2UgRGV2
# ZWxvcGVyLCBDZWRyaWMgQmxvbWFydDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBALp1WzPLBGmSjvJ2se9K+HWIzxlPIh9vmddaj0Sc6Yz3jJizVN7WphR7
# 1kR4KFEfCFL6eleZlIkqDMkNfwBTpfVCx7MYR85YSceHo8ieEIV5MmWSZ1CR36/A
# wXrUvXzlX85Rf/RuotfluSmdhTRoBbCN9aIsSm50pEZixvUOcipTNZbuY29NvjXP
# JAH1RX0od6QYCGt/v+5C+hBippk+QohxmKQYLilh4+58fdZvnXaKPvEuEpbhXCeh
# /HbASrvTbZXXbh8fMbHOq+xnCemK/6aqjfqgRzkXn9unjl6wj6KT7MoEp1tCn+9z
# cmi/zW3KeI3Alld5B6IEvVfZVmaCrvSsRBNFnVpVg5eGQNRyan0xfblEwKLrsoNk
# WhUtJY3SD53kvz8OHBKRY1quaq70UefcIDuPq4+76CVovjbpT/wnmP6PFnHtNrDj
# KzMAnUWOeseIcDhp7ETzVyoh2rWofTpXUr5aEau5mkXs0MclqjimMeWf0r3NyBa5
# E3k0rG/xtpmTFB9e4w7b0VQUxNUcVdmocGL7Rj4ouWLjUDUkiE2DctxMTaPmqKJE
# nx8cFsBCrDmgNYxGOZ03ykPmPbwc3SooZeS5rRmR4v2GTVrbsmbiiggtYDjhJddo
# sC/TtJ+65JL48oTHDK1KVMtSjUBr/Q16Nzg0JaXfiZ4JS+MX45ylAgMBAAGjggF4
# MIIBdDAMBgNVHRMBAf8EAjAAMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jY3Nj
# YTIwMjEuY3JsLmNlcnR1bS5wbC9jY3NjYTIwMjEuY3JsMHMGCCsGAQUFBwEBBGcw
# ZTAsBggrBgEFBQcwAYYgaHR0cDovL2Njc2NhMjAyMS5vY3NwLWNlcnR1bS5jb20w
# NQYIKwYBBQUHMAKGKWh0dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jY3NjYTIw
# MjEuY2VyMB8GA1UdIwQYMBaAFN10XUwA23ufoHTKsW73PMAywHDNMB0GA1UdDgQW
# BBQ2dBi+EnTsl++Xmrax5h7dyzC12TBLBgNVHSAERDBCMAgGBmeBDAEEATA2Bgsq
# hGgBhvZ3AgUBBDAnMCUGCCsGAQUFBwIBFhlodHRwczovL3d3dy5jZXJ0dW0ucGwv
# Q1BTMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG
# 9w0BAQsFAAOCAgEAZm3wL05QNFh25ll601jtST553L2VVS4ugvlHmX90PSOkjmX3
# jQ2ERwdz9MHWhoOvW1EdOrBlm31YRt2GmCz4/82vq9fTTNwLSzk3csHEIgHuAbN1
# INCVgz0l7Ya7mkxFLHoZ4BJ9LluS4p2NjvU9r7OGnBCgfFaLriC+qRY8QxCeMQOl
# l1BTTaUBMp0pgxh3XOOxOQfNeFCkXg0q4QULkaRuApJFZi/pY+PMA6p8bjOTP4YI
# 9VNsoc7ReBYMDL588oupc1CTiDaf0e6YaN80z6WoIGuNja3VPDEG4VmWZG+Of9gO
# XUNllvR8n1IXXsvWEuHRTCV9jjK89NHGaNroeXHr1C6eERnoNUPbEuIb/parUnGB
# n5MKWL+2TlL9Z34lSFz6e1Efi4oJeDznUojNZKJzOMvBS+JJyr0aVuW4lDoqbqkl
# 9vFLjHi1oGM7mBrlL8AHEC3iUNEOwgPcbsIYAqV85RyXYNQqLu1ik1MmmpKy/Tma
# Mmi/AiXxtjC6RrnrkN6tIHR7Nky9b5jWfMxCCG2b119gr7SkJ8qVOH8I9oBxMDPR
# IjBi5rEjSbDOCpRhZiqmx7K7gW2Gyp1OCIffVyiqmeZh+YLxpdP0tymxtqg/HVqV
# If3xn0Sgl7HcUpcRkY3EgTPUqNaVzOSO5h8KyzA9nVtUX2XwSx9C4vb6uqcxggMc
# MIIDGAIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBT
# eXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNpZ25pbmcgMjAyMSBD
# QQIQTZ+zbBZIHiQAJUisU7Eo1jANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3
# AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG
# AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCAeSVpEt1Xi
# 1nJamorDGe33dZkEDVAZmBNfyZboykFOUDANBgkqhkiG9w0BAQEFAASCAgA4jhO4
# 9X/1Z1acYDFmeUBCc0XhEgLlo5mNs82YhHXV07NRHO9IcL5kK6ISgxqe6tQNH8sD
# xwhcMw1Y3wMnYJV35um4Qt2qFaKQRUsE2oWhsYtK54Cx+Px/YxNt9118/Y+ujdEu
# ia1j6ZAYDsEowNg2VuP17s2TFWzTaM7e+fNf9buOibN4sM7w85v/1jfTGalNW8eu
# JcA0WWnDugbFpQhkIpCRrhAVtiYLPCB2YMKkGGj1wxfbWcfDJMLlXZi59jQMkp3J
# OuPshFTEoA1SrLELGFC0l01MBp5GmFFST9zswA0UBlfuV6Nn5KJ60faz8m9F99wm
# 16exCs+mYxKm+jHrKo9+pD+guk/BuQEaIHuBOryVmNNFkVLDV0euFhIzHYkousR0
# IYV8R9Vo2Ts2aw4frXOhfSIou4s0QdP1b4lBOOuWhHbI+JGt2t43y0bjQeIWt0Xc
# mv2q3mzuZFZqh92LJIxhl0vy/k7FbHjvZAq4JFF0VsAYkAZmZkzAOHCcp54lOg5y
# J2Q98d5uxm2BCwWnnT9SSUlmdLg6qxhy6DjG2AYIv2zeLc2GCdFUekbOL3MII+Ul
# ByznDH9jj0ct7TWqHtUkDqCdG8DhDc6rhC2LgcfETT1xEkV+freAj03w52ecIQo8
# IgSQNYJ9GEAhjL7jgyQHvlS1koFjr0vhglY91g==
# SIG # End signature block