When logged in to the Azure Portal, any application registration that has an expired secret is highlighted in the list. While this is helpful, most folks would appreciate if a notification of expiration happens before the credential has expired.
The applications registered in your tenant are accessible via Microsoft Graph as application resources. This resource has two properties that can be inspected for expiring credentials: passwordCredentials
and keyCredentials
. (Key Credentials are Certificates.)
The documentation for Microsoft Graph shows an example of using the any
clause in a $filter
query parameter, but I have not been able to make it work. (Or the applications
resource does not support it.) So I wrote C# to do this work for me! :)
The code is a sample the in Graph.Community repo. The interesting part of the code is the PageIterator processing that is part of the SDK.
I first define a method (technically a Func delegate) that will be invoked by the SDK for each item returned in the collection.
// equivalent to Func<Application, bool> iteratorItemCallback = (a) => {}
bool iteratorItemCallback(Application a)
{
// process the current item
if (a.PasswordCredentials.Any(c => c.EndDateTime < DateTime.UtcNow.AddDays(30)))
{
Console.WriteLine($"{a.DisplayName} ({a.AppId})");
}
// return true to indicate iteration should continue
return true;
}
(Checking the KeyCredentials is omitted for brevity.)
Next, a request is made to get the collection of applications. The Top()
method is used to specify the page size. I set it to the max value -- set it to 1 if you want to step thru the iterator and see it in action.
var results =
await graphServiceClient
.Applications
.Request()
.Top(999)
.GetAsync();
If there are more items in the collection than the limit specified in the Top()
method, the payload will contain a property named @odata.nextlink
. There is an entire page in the docs that describes paging. But the SDK does this work for us in the PageIterator<>
class.
The PageIterator<>
class has a factory method to which we pass the object type we expect, the first page of results, the GraphServiceClient (which it uses to fetch next pages), and the Func (delegate) to call for each item in the collection.
var appIterator =
PageIterator<Application>
.CreatePageIterator(graphServiceClient, results, iteratorItemCallback);
await appIterator.IterateAsync();
It is not used in the sample, but the PageIterator factory has an override to specify a
requestConfigurator
delegate that allows you adjust the next page request before it is sent.
The Graph.Community library has extensions to support APIs other than Microsoft Graph in addition to the samples. I encourage you to read about that capability: Graph-first programming in Microsoft 365
Ideas and Pull Requests are encouraged! Start by opening an Issue.