SOLVED

Unsupported but very useful way to use the hidden Azure API

%3CLINGO-SUB%20id%3D%22lingo-sub-175754%22%20slang%3D%22en-US%22%3EUnsupported%20but%20very%20useful%20way%20to%20use%20the%20hidden%20Azure%20API%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-175754%22%20slang%3D%22en-US%22%3E%3CP%3EObviously%20not%20supported%2C%20but%20for%20those%20of%20us%20managing%20many%20tenants%20and%2For%20setting%20up%20(test%2Ftraining%2Fpilot)%20tenants%20often%2C%20you%20may%20want%20to%20automate%20certain%20Azure%20AD%20or%20Intune%20settings%20that%20are%20not%20available%20through%20supported%20API's%20or%20PS%20modules%2C%20there%20is%20a%26nbsp%3B'hidden'%20API%20at%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Fmain.iam.ad.ext.azure.com%2Fapi%2FMdmApplications%2Feab0bcaf-9b2e-4e62-b9be-2eea708422f8%3FmdmAppliesToChanged%3Dtrue%26amp%3BmamAppliesToChanged%3Dtrue%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fmain.iam.ad.ext.azure.com%2Fapi%3C%2FA%3E%2C%20here's%20an%20example%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CA%20href%3D%22http%3A%2F%2Fwww.lieben.nu%2Fliebensraum%2F2018%2F03%2Fset-intune-mdm-user-scope-to-all-using-powershell-and-hidden-api%2F%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttp%3A%2F%2Fwww.lieben.nu%2Fliebensraum%2F2018%2F03%2Fset-intune-mdm-user-scope-to-all-using-powershell-and-hidden-api%2F%3C%2FA%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EIf%20you%20want%20to%20use%20this%20in%20a%20production%20environment%2C%20I%20recommend%20doing%20only%20READ%20operations.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-175754%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EAPI%20Management%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EAutomation%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EMonitoring%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E%3CLINGO-SUB%20id%3D%22lingo-sub-252545%22%20slang%3D%22en-US%22%3ERe%3A%20Unsupported%20but%20very%20useful%20way%20to%20use%20the%20hidden%20Azure%20API%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-252545%22%20slang%3D%22en-US%22%3E%3CP%3EFantastic%20information!!!%20Thank%20you%20for%20sharing!!%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20have%20discovered%20COUNTLESS%20uses%20for%20this%2C%20from%20obtaining%20all%20of%20the%20'Conditional%20Access'%20policies%20that%20are%20configured%20in%20AzureAD%2C%20to%20obtaining%20a%20list%20of%20Azure%20Gallery%2FMarketplace%20Apps%20that%20are%20available%20as%20'Enterprise%20Apps'.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThanks%20again!%3C%2FP%3E%3CP%3EEvan%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1113200%22%20slang%3D%22en-US%22%3ERe%3A%20Unsupported%20but%20very%20useful%20way%20to%20use%20the%20hidden%20Azure%20API%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1113200%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F53091%22%20target%3D%22_blank%22%3E%40Jos%20Lieben%3C%2FA%3E%20The%20hidden%20API%20link%20throws%20unauthorized%20error.%26nbsp%3B%20Is%20it%20suppose%20to%3F%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1166151%22%20slang%3D%22en-US%22%3ERe%3A%20Unsupported%20but%20very%20useful%20way%20to%20use%20the%20hidden%20Azure%20API%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1166151%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F35281%22%20target%3D%22_blank%22%3E%40Malathi%20Sekkappan%3C%2FA%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EOpen%20a%20Browser%20like%20Firefox%20or%20Chrome%2C%20open%20the%20Developer%20Tools%20(F12)%2C%20do%20the%20change%20you%20want%20to%20do%20by%20the%20API%20in%20the%20GUI%20on%20portal.azure.com%2C%20and%20check%20what's%20going%20on%20on%20the%20Network-Tab%20of%20the%20Developer%20Tools%2C%20because%20the%20GUI%20is%20calling%20the%20same%20API.%3C%2FP%3E%3CP%3EAnd%20there%20you'll%20see%20the%20call%20to%20the%20API%20with%20all%20the%20headers%20required%3C%2FP%3E%3CP%3E%3CSPAN%3EJust%20lookout%20for%20the%20call%20to%20%3CA%20href%3D%22https%3A%2F%2Fmain.iam.ad.ext.azure.com%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3Ehttps%3A%2F%2Fmain.iam.ad.ext.azure.com%3C%2FA%3E%3C%2FSPAN%3E%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%3CSPAN%3EPS.%20Don't%20forget%20to%20provide%20the%20Bearer%20Token%20and%20the%26nbsp%3B%3CFONT%20face%3D%22courier%20new%2Ccourier%22%3Ex-ms-client-request-id%3C%2FFONT%3E%3C%2FSPAN%3E%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-2128757%22%20slang%3D%22en-US%22%3ERe%3A%20Unsupported%20but%20very%20useful%20way%20to%20use%20the%20hidden%20Azure%20API%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2128757%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F35281%22%20target%3D%22_blank%22%3E%40Malathi%20Sekkappan%3C%2FA%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EHere%20a%20few%20quick%20PowerShell%20samples%20that%20I've%20used%20previously%3A%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powershell%22%3E%3CCODE%3E%24adalversion%20%3D%20%222.28.4%22%0A%24aadgraph%20%3D%20%221.61-internal%22%0A%0A%24adal%20%3D%20resolve-path%20%22%24pwd%5C.nuget%5Cpackages%5CMicrosoft.IdentityModel.Clients.ActiveDirectory.%24adalversion%5Clib%5Cnet45%22%0A%5BSystem.Reflection.Assembly%5D%3A%3ALoadFrom(%22%24adal%5CMicrosoft.IdentityModel.Clients.ActiveDirectory.dll%22)%0A%5BSystem.Reflection.Assembly%5D%3A%3ALoadFrom(%22%24adal%5CMicrosoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll%22)%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EAnd%2C%20once%20you%20have%20authenticated%20properly%20using%20the%20below%20method%20(NOTE%20the%20specific%20version%20of%20the%20ADAL%20client%20libraries%20being%20used!!!)%20and%20have%20your%20access%20token%2C%20you%20can%20create%20a%20PowerShell%20function%20to%20retrieve%20Conditional%20Access%20policies%2C%20Gallery%20Apps%2C%20Grant%20Admin%20Consent%20to%20apps%20programmatically%2C%20etc.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EExample%20functions%20to%20Get%20an%20access%20token%20to%20the%20'hidden'%20API%20and%20an%20example%20function%20to%20retrieve%20Gallery%20apps%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powershell%22%3E%3CCODE%3EFunction%20Get-PortalAPIAccessToken%20%7B%0A%20%20%20%20param(%0A%20%20%20%20%20%20%20%20%5Bstring%5D%24clientId%20%3D%20%221950a258-227b-4e31-a9cf-717495945fc2%22%2C%0A%20%20%20%20%20%20%20%20%5Bstring%5D%24redirectUri%20%3D%20%22urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob%22%0A%20%20%20%20)%0A%20%20%20%20%24audgid%20%3D%20%2274658136-14ec-4630-ad9b-26e160ff0fc6%22%0A%20%20%20%20%24authority%20%3D%20%22https%3A%2F%2Flogin.microsoftonline.com%2F%3CTENANT%3E%22%0A%20%20%20%20%24authContext%20%3D%20New-Object%20%22Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext%22%20-ArgumentList%20%24authority%2C%20%24false%0A%20%20%20%20%24authResult%20%3D%20%24authContext.AcquireToken(%24audgid%2C%20%24clientId%2C%20%24redirectUri%2C%20'Auto')%0A%20%20%20%20%24token%20%3D%20%24authResult.AccessToken%0A%20%20%20%20return%20%24token%3B%0A%7D%0A%0Afunction%20Get-GalleryApps()%20%7B%0A%20%20%20%20Param(%0A%20%20%20%20%20%20%20%20%5BParameter()%5D%24token%2C%0A%20%20%20%20%20%20%20%20%5BParameter()%5D%24nextLink%0A%20%20%20%20)%0A%20%20%20%20%24global%3Aapps%20%3D%20%40()%0A%20%20%20%20%24header%20%3D%20%40%7B%0A%20%22Sec-Fetch-Dest%22%20%20%3D%20%22empty%22%3B%0A%20%22Sec-Fetch-Mode%22%20%20%3D%20%22cors%22%3B%0A%20%20%20%20%20%20%20%20%22accept-encoding%22%20%20%20%20%20%20%20%20%3D%20%22gzip%2C%20deflate%2C%20br%22%3B%0A%20%20%20%20%20%20%20%20%22accept-language%22%20%20%20%20%20%20%20%20%3D%20%22en%22%3B%0A%20%20%20%20%20%20%20%20%22x-ms-effective-locale%22%20%20%3D%20%22en.en-us%22%0A%20%20%20%20%20%20%20%20%22Authorization%22%20%20%20%20%20%20%20%20%20%20%3D%20%22Bearer%20%24token%22%3B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%20%20%20%20%20%20%20%20%20%20%20%3D%20%22application%2Fjson%22%3B%0A%20%20%20%20%20%20%20%20%22x-ms-client-request-id%22%20%3D%20(New-Guid).Guid%3B%0A%20%20%20%20%20%20%20%20%22x-ms-session-id%22%20%20%3D%20%2212345678910111213141516%22%3B%0A%20%20%20%20%20%20%20%20%22Accept%22%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%20%22*%2F*%22%3B%0A%20%20%20%20%20%20%20%20%22x-requested-with%22%20%20%20%20%20%20%20%3D%20%22XMLHttpRequest%22%3B%0A%20%20%20%20%20%20%20%20%22user-agent%22%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%20%22Mozilla%2F5.0%20(Windows%20NT%2010.0%3B%20Win64%3B%20x64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F77.0.3829.0%20Safari%2F537.36%20Edg%2F77.0.197.1%22%3B%0A%20%20%20%20%20%20%20%20%22method%22%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%20%22GET%22%0A%20%20%20%20%7D%0A%20%20%20%20%24url%20%3D%20%22https%3A%2F%2Fmain.iam.ad.ext.azure.com%2Fapi%2Fapplications%2Fgallery%3Ftop%3D999%26amp%3BnextLink%3D%24nextLink%22%0A%20%20%20%20%24global%3Ares%20%3D%20Invoke-RestMethod%20-Uri%20%24url%20-Headers%20%24header%20-Method%20GET%20-ContentType%20%22application%2Fjson%22%0A%20%20%20%20foreach%20(%24global%3Aapp%20in%20%24global%3Ares.items)%20%7B%0A%20%20%20%20%20%20%20%20%24global%3Aapps%20%2B%3D%20(%24global%3Aapp%20%7C%20ConvertTo-Json%20-Compress)%20-join%20%22%2C%22%0A%20%20%20%20%7D%0A%20%20%20%20if%20(%24global%3Ares.nextLink)%20%7B%0A%20%20%20%20%20%20%20%20%24global%3Aapps%20%2B%3D%20((get-galleryapps%20-nextLink%20%24global%3Ares.nextLink)%20%7C%20ConvertTo-Json%20-Compress)%20-join%20%22%2C%22%0A%20%20%20%20%7D%0A%20%20%20%20%24apps%20%7C%20ConvertTo-Json%20-Compress%20%7C%20Out-File%20%22.%5Cgalleryapplist.json%22%20-Force%20-Append%0A%20%20%20%20return%20%24global%3Aapps%0A%7D%3C%2FTENANT%3E%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-2128903%22%20slang%3D%22en-US%22%3ERe%3A%20Unsupported%20but%20very%20useful%20way%20to%20use%20the%20hidden%20Azure%20API%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2128903%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F193181%22%20target%3D%22_blank%22%3E%40Evan%20Bachert%3C%2FA%3E%3C%2FP%3E%3CP%3EI%20would%20recommend%20using%20method%202%20in%20this%20post%20instead%2C%20then%20you%20don't%20need%20DLL's%20%2F%20modules%20at%20all%3A%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Fwww.lieben.nu%2Fliebensraum%2F2020%2F04%2Fcalling-graph-and-other-apis-silently-for-an-mfa-enabled-account%2F%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fwww.lieben.nu%2Fliebensraum%2F2020%2F04%2Fcalling-graph-and-other-apis-silently-for-an-mfa-enabled-account%2F%3C%2FA%3E%3C%2FP%3E%3C%2FLINGO-BODY%3E
Contributor

Obviously not supported, but for those of us managing many tenants and/or setting up (test/training/pilot) tenants often, you may want to automate certain Azure AD or Intune settings that are not available through supported API's or PS modules, there is a 'hidden' API at https://main.iam.ad.ext.azure.com/api, here's an example:

 

http://www.lieben.nu/liebensraum/2018/03/set-intune-mdm-user-scope-to-all-using-powershell-and-hidde...

 

If you want to use this in a production environment, I recommend doing only READ operations.

5 Replies
best response confirmed by Jos Lieben (Contributor)
Solution

Fantastic information!!! Thank you for sharing!!

 

I have discovered COUNTLESS uses for this, from obtaining all of the 'Conditional Access' policies that are configured in AzureAD, to obtaining a list of Azure Gallery/Marketplace Apps that are available as 'Enterprise Apps'.

 

Thanks again!

Evan

@Jos Lieben The hidden API link throws unauthorized error.  Is it suppose to?

@Malathi Sekkappan 

 

Open a Browser like Firefox or Chrome, open the Developer Tools (F12), do the change you want to do by the API in the GUI on portal.azure.com, and check what's going on on the Network-Tab of the Developer Tools, because the GUI is calling the same API.

And there you'll see the call to the API with all the headers required

Just lookout for the call to https://main.iam.ad.ext.azure.com

 

PS. Don't forget to provide the Bearer Token and the x-ms-client-request-id

@Malathi Sekkappan 

 

Here a few quick PowerShell samples that I've used previously:

 

$adalversion = "2.28.4"
$aadgraph = "1.61-internal"

$adal = resolve-path "$pwd\.nuget\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.$adalversion\lib\net45"
[System.Reflection.Assembly]::LoadFrom("$adal\Microsoft.IdentityModel.Clients.ActiveDirectory.dll")
[System.Reflection.Assembly]::LoadFrom("$adal\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll")

 

And, once you have authenticated properly using the below method (NOTE the specific version of the ADAL client libraries being used!!!) and have your access token, you can create a PowerShell function to retrieve Conditional Access policies, Gallery Apps, Grant Admin Consent to apps programmatically, etc.

 

Example functions to Get an access token to the 'hidden' API and an example function to retrieve Gallery apps:

Function Get-PortalAPIAccessToken {
    param(
        [string]$clientId = "1950a258-227b-4e31-a9cf-717495945fc2",
        [string]$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
    )
    $audgid = "74658136-14ec-4630-ad9b-26e160ff0fc6"
    $authority = "https://login.microsoftonline.com/<tenant>"
    $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority, $false
    $authResult = $authContext.AcquireToken($audgid, $clientId, $redirectUri, 'Auto')
    $token = $authResult.AccessToken
    return $token;
}

function Get-GalleryApps() {
    Param(
        [Parameter()]$token,
        [Parameter()]$nextLink
    )
    $global:apps = @()
    $header = @{
	"Sec-Fetch-Dest"	 = "empty";
	"Sec-Fetch-Mode"	 = "cors";
        "accept-encoding"        = "gzip, deflate, br";
        "accept-language"        = "en";
        "x-ms-effective-locale"  = "en.en-us"
        "Authorization"          = "Bearer $token";
        "Content-Type"           = "application/json";
        "x-ms-client-request-id" = (New-Guid).Guid;
        "x-ms-session-id"	 = "12345678910111213141516";
        "Accept"                 = "*/*";
        "x-requested-with"       = "XMLHttpRequest";
        "user-agent"             = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3829.0 Safari/537.36 Edg/77.0.197.1";
        "method"                 = "GET"
    }
    $url = "https://main.iam.ad.ext.azure.com/api/applications/gallery?top=999&nextLink=$nextLink"
    $global:res = Invoke-RestMethod -Uri $url -Headers $header -Method GET -ContentType "application/json"
    foreach ($global:app in $global:res.items) {
        $global:apps += ($global:app | ConvertTo-Json -Compress) -join ","
    }
    if ($global:res.nextLink) {
        $global:apps += ((get-galleryapps -nextLink $global:res.nextLink) | ConvertTo-Json -Compress) -join ","
    }
    $apps | ConvertTo-Json -Compress | Out-File ".\galleryapplist.json" -Force -Append
    return $global:apps
}

 

@Evan Bachert

I would recommend using method 2 in this post instead, then you don't need DLL's / modules at all: https://www.lieben.nu/liebensraum/2020/04/calling-graph-and-other-apis-silently-for-an-mfa-enabled-a...