Search code examples
azurepowershellmicrosoft-graph-api

How to get PIM role assignments for children resources of a subscription via PowerShell in Azure?


I'm having a hard time pulling PIM assignments for every child resource within a subscription in Azure.

I'm currently using Get-AzureADMSPrivilegedRoleAssignment from the AzureADPreview module, like below:

$allRolesSingleSubAssignment = Get-AzureADMSPrivilegedRoleAssignment -ProviderId AzureResources -ResourceId $sub.Id

This is returning everything at the provided -ResourceId level, which in my case is a subscription. However, I'm trying to return the PIM assignments for everything that is a child resource of that sub as well, exactly how it allows you to do in the portal, as in the screenshot below.

Azure PIM pull all

Whereas currently the command I shared above is providing only the first option "Export members only in this subscription". I'm a bit at a loss here and am wondering if it's not available via PowerShell? I'd prefer to be able to pull this via PowerShell if possible, but would appreciate any suggestions direct from the Graph side as well.

So far I haven't been able to find a way to pull what I'm looking for, so any help or tips is much appreciated!


Solution

  • After investigating what call the Portal was making through the browser Developer Tools, I saw this:

    https://api.azrbac.mspim.azure.com/api/v2/privilegedAccess/azureResources/roleAssignments/exportWithChildren
    

    That call from the portal returns a CSV with the requested data. However, I built the below function(s) to give you the option to output to CSV or just leverage the data in script:

    function Set-HeaderWithToken {
        $pimtoken = Get-AzAccessToken -ResourceUrl '01fc33a7-78ba-4d2f-a4b7-768e336e890e' <#MS-PIM#> -ErrorAction Stop
        $headers = @{
            "Authorization" = "Bearer {0}" -f ($pimtoken.Token)
        }
        return $headers
    }
    
    function Query-PIMObjectWithChildren {
        #Returns PIM objects for both Azure Resource and AAD roles. If you pass aadRoles for the $roleType param, you do not need to specify $resourceIdentifier as it will default to the tenant ID needed for that call, since there's only one call to make for the AAD roles.
        param(
            [Parameter(Mandatory)]
            $header,
            [Parameter(Mandatory)]
            [ValidateSet("azureResources", "aadRoles")] #Passed parameter can only be one of these two options
            [string] $roleType,
            [string] $resourceIdentifier,
            [Parameter(Mandatory)]
            [ValidateSet("yes", "no")] #Passed parameter can only be one of these two options
            [string] $outputToCSV,
            [string] $subName
        )
    
        if ($roleType -eq "aadRoles") {
            $resourceIdentifier = "<tenant id>"
            $subName = "Tenant"
            $APIUri = "https://api.azrbac.mspim.azure.com/api/v2/privilegedAccess/aadRoles/roleAssignments/exportWithChildren"
        }
        elseif ($roleType -eq "azureResources") {
            $APIUri = "https://api.azrbac.mspim.azure.com/api/v2/privilegedAccess/azureResources/roleAssignments/exportWithChildren"
        }
    
        $parameters = @{
            '$expand' = 'subject,roleDefinition($expand=resource)'
            '$filter' = "(roleDefinition/resource/id eq '$resourceIdentifier')"
        }
    
        if ($outputToCSV -eq "yes") {
            Invoke-WebRequest -Headers $header -Uri $APIUri  -Method Get -Body $parameters -OutFile "C://Output.csv"
        }
        elseif ($outputToCSV -eq "no") {
            $result = Invoke-WebRequest -Headers $header -Uri $APIUri  -Method Get -Body $parameters -UseBasicParsing
    
            #Decode the raw result data as UTF-8 and skip the BOM (2 BOMs)
            $stringDecoded = [Text.Encoding]::UTF8.GetString($result.RawContentStream.ToArray()).Substring(2)
    
            #Now that it is decoded properly, we can convert to PSObjects
            $properResult = $stringDecoded | ConvertFrom-Csv
    
            return $properResult
        }
    }
    
    #Grab token and set header
    $header = Set-HeaderWithToken
    
    <# Need to have a list of subs and the id of them for the below to work.
    foreach ($sub in $subscriptionList){
    
        $subPIMId = (Get-AzureADMSPriviliegedResource -ProviderId 'AzureResources' -Filter "ExternalId eq //subscriptions/$subscriptionId/'").Id
    }
    #>
    
    $finalResult = Query-PIMObjectWithChildren -header $header -roleType azureResources -resourceIdentifier "xxxxx-xxx-xxxx-xxxx-xxxxxxxxxxx" -outputToCSV no
    

    The -resourceIdentitfier switch above for the "Query-PIMOBjectWithChildren" function / API call is a PIM specific ID for the resource. To clarify, it is not the ID you are used to using. IE, if you are trying to pull the data for a subscription, it is not the subscription ID.

    You can find the correct ID in one of two ways:

    1. The PIM blade in the portal for Azure resources. Select your resource and then note the ID in the URL: resourceIdentifier for PIM resource

    2. Use the Get-AzureADMSPrivilegedResource command to find the ID. Example below:

      $subPIMId = (Get-AzureADMSPriviliegedResource -ProviderId 'AzureResources' -Filter "ExternalId eq //subscriptions/$subscriptionId/'").Id

    *Note: Subscription Id above is the true subID you are used to dealing with. The output of the above command (I'm accessing the .ID property) can then be fed into the -resourceIdentifier switch to generate the PIM data with all the children beneath it.

    One last note, the same thing can be performed for grabbing all AAD role assignments as well. Though it's just at the tenant level, so it's not as helpful as the Azure RBAC side was for me since that data for AAD roles can be generated other ways.