Functions/Get-AGSPEntraMemberOf.ps1
|
Function Get-AGSPEntraMemberOf{ <# .SYNOPSIS Retrieves the Entra (Azure AD) groups and directory roles that a service principal is a direct member of via MS Graph API. .DESCRIPTION Retrieves the Entra (Azure AD) groups and directory roles that a service principal is a direct member of via MS Graph API. To see transitive memberships (including nested groups), use the -UseBetaAPI switch. .EXAMPLE $AccessToken = Get-AGGraphAccessToken -TenantID $TenantID -ClientID $ClientId -ClientSecret $ClientSecret Get-AGSPEntraMemberOf -AccessToken $AccessToken -ObjectID "12345678-1234-1234-1234-123456789abc" This command first gets an access token, which is used to grant access to Graph, and then retrieves the Entra entries (groups and directory roles) that the specified service principal is a direct member of. .EXAMPLE Get-AGSPEntraMemberOf -AccessToken $AccessToken -AppID "12345678-1234-1234-1234-123456789abc" This command uses the Application (Client) ID instead of the Object ID to look up the service principal and retrieve its direct memberships. .EXAMPLE Get-AGSPEntraMemberOf -AccessToken $AccessToken -ObjectID "12345678-1234-1234-1234-123456789abc" -UseBetaAPI This command retrieves transitive memberships (including nested groups) for the specified service principal using the beta endpoint. .PARAMETER AccessToken This is the access token that grants you access to Microsoft Graph. If omitted, the function uses the module-scoped token created by Get-AGGraphAccessToken or Get-AGGraphAccessTokenFromAz. .PARAMETER ObjectID This is the Object ID (OID) of the service principal you want to check memberships for. Either -ObjectID or -AppID must be provided. .PARAMETER AppID This is the Application (Client) ID of the app registration you want to check memberships for. The function will look up the corresponding service principal. Either -ObjectID or -AppID must be provided. .PARAMETER UseBetaAPI This will force use of the beta version of the API, which uses the /transitiveMemberOf endpoint. This shows all groups the service principal is a member of, including nested memberships. As with all other "beta things" use with caution. Or reckless abandon. Be yourself. .INPUTS None. You cannot pipe input to this function. .OUTPUTS A collection of directory objects representing groups and directory roles that the service principal is a member of. .NOTES Author: Lars Panzerbjørn #> [CmdletBinding(DefaultParameterSetName='AppID')] param ( [Parameter(ParameterSetName='ObjectID')] [Parameter(ParameterSetName='ObjectIDBeta')] [Parameter(ParameterSetName='AppID')] [Parameter(ParameterSetName='AppIDBeta')] [psobject]$AccessToken, [Parameter(Mandatory=$true,ParameterSetName='ObjectID')] [Parameter(Mandatory=$true,ParameterSetName='ObjectIDBeta')] [string]$ObjectID, [Parameter(Mandatory=$true,ParameterSetName='AppID')] [Parameter(Mandatory=$true,ParameterSetName='AppIDBeta')] [string]$AppID, [Parameter(Mandatory=$true,ParameterSetName='ObjectIDBeta')] [Parameter(Mandatory=$true,ParameterSetName='AppIDBeta')] [switch]$UseBetaAPI ) BEGIN{ IF (($AccessToken) -or ($TokenResponse)){ IF($AccessToken){$Headers = @{Authorization = "Bearer $($AccessToken.access_token)"}} IF(!($AccessToken)){$Headers = @{Authorization = "Bearer $($TokenResponse.access_token)"}} } ELSE {THROW "Please provide access token"} IF($UseBetaAPI){$Version = "/beta"} Else {$Version = "/v1.0"} $BaseURI = "https://graph.microsoft.com" } PROCESS{ # If AppID was provided, first look up the service principal to get its Object ID IF($PSCmdlet.ParameterSetName -eq "AppID" -or $PSCmdlet.ParameterSetName -eq "AppIDBeta"){ Write-Verbose "Looking up service principal with AppID: $AppID" $LookupURI = $BaseURI + "/v1.0/servicePrincipals(appId='$AppID')" $LookupResult = Invoke-RestMethod -Uri $LookupURI -Headers $Headers IF($LookupResult.value){ $OID = $LookupResult.value.id Write-Verbose "Found service principal with ObjectID: $OID" } ELSEIF($LookupResult.id){ $OID = $LookupResult.id Write-Verbose "Found service principal with ObjectID: $OID" } ELSE{ THROW "No service principal found with AppID: $AppID" } } ELSEIF($PSCmdlet.ParameterSetName -eq "ObjectID" -or $PSCmdlet.ParameterSetName -eq "ObjectIDBeta"){ $OID = $ObjectID } ELSE { THROW "Either -ObjectID or -AppID must be provided" } # Build the appropriate endpoint URI IF($UseBetaAPI){ $ExpandedURI = "/servicePrincipals/$OID/transitiveMemberOf" } ELSE{ $ExpandedURI = "/servicePrincipals/$OID/memberOf" } $URI = $BaseURI + $Version + $ExpandedURI Write-Verbose "Querying: $URI" $Result = Invoke-RestMethod -Uri $URI -Headers $Headers $Resources = $Result.value IF (!([string]::IsNullOrEmpty($Result.'@odata.nextLink'))){ $Page = 1 DO{ Write-Verbose "Page $($Page)" $URI = $Result.'@odata.nextLink' $Result = Invoke-RestMethod -Uri $URI -Headers $Headers $Resources += $Result.value Write-Verbose "There are $($Resources.count) resources" $Page++ } UNTIL ([string]::IsNullOrEmpty($Result.'@odata.nextLink')) } Write-Verbose "There are $($Resources.count) resources" } END{ Return $Resources } } |