Fun with Azure Functions: Building a lookup tool for Azure AD tenant IDs

Fun with Azure Functions: Building a lookup tool for Azure AD tenant IDs

I sometimes find myself struggling with a technical issue that I just can’t seem to resolve in a timely manner. Over the years I’ve learned that once you’ve spent maybe 15 to 30 minutes troubleshooting without any noticeable progress, there’s only two things that will help you: a huge cup of fresh coffee or working on something else for a while. Some people claim asking for help from others is also beneficial, but I haven’t dared to go that far yet.

I started working on something else. I found a problem where an external consultant working on a project for my company was unable to perform a task in Azure DevOps. Turns out this person didn’t have enough permissions to configure something as it was a guest account within our Azure AD tenant. The solution that was proposed assumed I was comfortable in granting co-administrator permissions for this guest account. It would have been so easy, just one click and the issue would go away, and I could return to work on my original problem.

I chose to instead perform the configuration with my administrative account. One of the selections in configuring this specific setting in Azure DevOps asks for the tenant ID. As I often find people mixing up the tenant ID, subscription ID, Azure Active Directory ID and Office 365 tenant ID I think it makes sense to clarify these first.

  • Tenant ID: Azure Active Directory ID. As you can have multiple Azure AD tenants, you therefore can have multiple IDs. One Azure AD tenant can serve multiple Office 365 and Azure subscriptions.
  • Subscription ID: Azure subscription ID. You can have multiple Azure subscriptions, each with their own Azure AD tenants, or a single Azure AD tenant serving them all.
  • Directory ID: See: tenant ID above

You can easily look up your tenant ID through the Azure Portal by navigating to https://portal.azure.com > Azure AD > Properties. Direct URL is https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Properties. I sometimes see people also using the more restricted Azure AD portal view at https://aad.portal.azure.com.

Directory ID equals Tenant ID in this view

It’s easy to look up the tenant ID through here, and to look up the Azure Subscription IDs through Azure Portal > Subscriptions. Direct URL is https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade

The problem

I find it tedious to keep navigating here for a simple copy-paste exercise whenever I need my tenant ID.

Thankfully, the awesome people at ShareGate (the makers of the equally awesome Overcast, see my earlier post here) took the time and effort to come up with a free tool to look up your tenant ID – What is my tenant ID?

It resolves any {tenant}.onmicrosoft.com to the corresponding Azure AD Tenant ID. For free!

How does it work?

Back to my original problem — getting my mind off from a trickier problem and figuring out something less tricky. I admire ShareGate and this tool is a very clever idea. I wanted to understand just how it’s possible to resolve the tenant ID from a tenant name. Perhaps it’s based on a DNS query? I did a few guesses against DNS but quickly realized it wouldn’t be there, or at least it wouldn’t make any sense to store this type of data in DNS.

As I can use the tool to look up any tenant ID there has to be a public API hosted somewhere that I can employ. Without wanting to reverse-engineer what ShareGate had built, I went hunting for this data from Microsoft Graph.

Microsoft Graph is the one API to rule them all. Sometimes it provides a wealth of data, other times it doesn’t expose something you feel should be exposed. It’s a sort of wrapper and collection of APIs together that you can easily scavenge with Graph Explorer. Trawling through the Graph documentation I couldn’t find anything I could use without first authenticating against Azure AD. And that didn’t make sense as I knew I could simply query something to get a desired Azure AD tenant ID.

And then I recalled the very long evenings a few years ago I spent figuring out OpenID, and the Microsoft identity platform. I even bought the book and read it. And there it is — metadata for an app to perform sign-in. By performing a simple call to https://login.windows.net/{tenant-name}/v2,0/.well-known/openid-configuration I should be able to fetch useful metadata about any Azure AD tenant.

To verify, I opened a new incognito browser session and tried accessing https://login.microsoftonline.com/foo.onmicrosoft.com/v2.0/.well-known/openid-configuration:

Test call to resolve the Azure AD tenant ID

And it shows many interesting IDs — especially the authorization_endpoint which looks like the one I’m searching for. It’s also the same ID I’m getting through ShareGate’s lookup tool.

Building my own tool using Azure Functions and Visual Studio 2017

After this small bit of detective work, I knew it was easy to build a tool around this. I contemplated on creating a simple PowerShell cmdlet, but I felt that would be too boring. A real tool needs to have a real API — and for that I tend to use Azure Functions. It’s a serverless approach in Azure, so it also means it’s modern and hopefully earns me a bit more street credibility.

As you can create Azure Functions ad hoc using browser-based development, I wanted to use a real development approach and fired up Visual Studio 2017. I had already installed the Azure Functions tooling — you can get it through the Extensions and Updates tool within Visual Studio.

I was raised on C#, so that’s quite natural for me. I promised myself I wouldn’t self-generate another technical problem and chose the path of least resistance.

As the endpoint returns a simple JSON dataset, all I need is to perform a call with a tenant name and parse the response. So easy!

I’m also very rusty on JSON parsing intricacies, so I tend to default to Newtonsoft’s Json library. First, I need to define the parameter as a querystring:

// parse query parameter
string tenantName = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "tenantName", true) == 0).Value;

Next, I construct a simple HttpClient to do my HTTP call using asynchronous calls. This way my code is not blocking while it’s waiting for login.windows.net to resolve my query.

// call Microsoft Graph to resolve ID 
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string tenantID = "";
var url = "https://login.windows.net/" + tenantName + ".onmicrosoft.com/v2.0/.well-known/openid-configuration";

And finally I’ll just perform a HTTP call asynchronously, pick up the response content (that I hope is JSON) and use C#’s excellent dynamic object to retrieve only the value that I’m interested in. This saves me a bit of trouble and allows me to quickly deserialize the object:

 var response = await client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
dynamic json = JsonConvert.DeserializeObject(content);
tenantID = json.authorization_endpoint;
tenantID = tenantID.Substring(26, 37);

To run this locally a simple F5 is enough.

And I can even try it out locally before pushing it to the cloud:

That’s it! Publishing to Azure is simple with the built-in Publishing Wizard:

I chose Run from ZIP (recommended) as it was recommended. Apparently, this provides a few advantages such as improving performance for deployments thus making future updates nicer.

I can now use my own lookup tool directly by calling https://tenantidlookup.azurewebsites.net/api/TenantIDLookup?tenantName={tenantName}.

As the API is so simple it also becomes versatile to use. From PowerShell, I can use Invoke-RestMethod to call it:

Invoke-RestMethod -Method GET -UseBasicParsing -Uri https://tenantidlookup.azurewebsites.net/api/TenantIDLookup?tenantName=foo

Windows 10 also includes cURL by default (since 17063), so that can also be used to access the Azure Function-based API:

curl https://tenantidlookup.azurewebsites.net/api/TenantIDLookup?tenantName=foo

Contribute and reuse this code

I’ve pushed all source code for this project to my repo in GitHub. You can access the repository here.

In summary

This was a fun small project with Azure Functions. Serverless computing provides so much agility, versatility and happiness that it’s hard not to like this modern approach!

My goal was not to replace ShareGate’s own tool, but to learn from their great idea and implementation. When you understand how something works it typically results in being able to do your work a little bit better once you know what happens behind the curtains. And, it’s fun.