Tech Blog

Listing Subscriptions and Logic Apps from .NET

For most people, using PowerShell scripts/cmdlets to access Azure and get information
about subscriptions and resources (e.g. Logic Apps) will be enough.

But what if you want to do it yourself from .NET?

Luckily there are some good management libraries you can use. However, there
aren’t a huge amount of samples out there on how to use those management libraries.

So I thought I’d provide a guide on how to do the following: Log into Azure,
get a list of subscriptions, select a subscription, and then get a list of LogicApps
associated with that subscription.

The second part of this I covered in an earlier post on how to use the Logic
Apps Management SDK. But I soon discovered that the Azure Management Libraries and
the Logic Apps Management Library use a slightly different credential format.

The Demo App

Here’s what the app we’re creating is going to look like:

1) How to login

Normally when you login to Azure, you have to know what AAD Tenant to authenticate
against (see the sidebar below for an overview of AAD and tenants).

However, Azure has the concept of a multi-tenant login i.e. a login where you
don’t know which AAD Tenant you’re using: so you login using the common tenant.

When you login, you have to specify the ID of the client you’re using. Normally
in AAD you’d set up a client in your directory, and then authenticate against the
access rights given to that client.

This obviously wouldn’t work for a common tenant, so in the special case of
the common tenant, Azure has a list of common clients you can use. One of these is
the PowerShell client: when we use this client id, Azure assumes that we’re a PowerShell
script trying to authenticate against Azure (you can see this in the login window,
as it will say
Sign in to Microsoft Azure PowerShell in
the window title).

Logging in using the common tenant gives us limited permissions, but one of
the things we *can* do is get a list of the subscriptions that the current user can
access.

Logging in is as simple as running this code:

string TokenAudience = “https://management.core.windows.net/” ;

string ClientRedirectUri = “urn:ietf:wg:oauth:2.0:oob” ;

string ClientId = “1950a258-227b-4e31-a9cf-717495945fc2” ;

string EnableMagicCookie = “site_id=501358&display=popup” ;

string BaseLoginUrl = “https://login.windows.net/{0}” ;

string TenantId = “common” ;

AuthenticationResult result = null ;

AuthenticationContext authenticationContext
=
new AuthenticationContext ( string .Format(BaseLoginUrl,
TenantId),
true , TokenCache .DefaultShared);

try

{

result = authenticationContext.AcquireToken(

TokenAudience,

ClientId,

new Uri (ClientRedirectUri),

PromptBehavior .RefreshSession,

UserIdentifier .AnyUser,

EnableMagicCookie);

}

catch ( AdalException )

{

result = null ;

}

return result;

Some explanations:

  • TokenAudience is what we’re
    authenticating against i.e. the service we wish to use – in this case, we want to
    use the Management SDK.
  • ClientRedirectUri is set to the standard redirect URI
    for OAuth2.0 calls (this can also be used to redirect to a particular resource upon
    successful login).
  • ClientId is the special
    ID for the Azure PowerShell client.
  • EnableMagicCookie is an
    extra query parameter added to the end of the web request used to open the login window,
    that forces the window to try and use the Email Address method of determining if the
    user is an Org Account or Microsoft Account (see sidebar later for more on this).
  • BaseLoginUrl is the login URL to use to login to Azure
    (with a placeholder for the tenant id).
  • TenantId is the id of the AAD tenant we wish to use
    (which in this case is Common, as we don’t know which tenant to use just yet).

What is returned to us is an AuthenticationResult object
that will have a JWT (aka Bearer Token) in the
AccessToken property
(see the sidebar later for an explanation of JWTs/Bearer Tokens).

This AccessToken is what we pass to our
Management Library method calls, and later to our LogicApps SDK methods calls.

2) How to get a list of subscriptions

Luckily, with the Management Library, this is really easy:

CancellationToken canToken = new CancellationToken ();

TokenCloudCredentials cred = new TokenCloudCredentials (commonTenantCredentials.AccessToken);

SubscriptionClient client = CloudContext .Clients.CreateSubscriptionClient(cred);

SubscriptionListOperationResponse response
=
await client.Subscriptions.ListAsync(canToken);

// Project subscriptions into a list of SubscriptionWrapper
instances

List SubscriptionWrapper >
subscriptions = response.Subscriptions.Select

(

sub =>

new SubscriptionWrapper (sub)

).ToList();

There are three main parts to the code above:

  1. We need to create a new TokenCloudCredentials instance,
    which is what we pass to the management library – we instantiate this using our JWT
    token stored in the
    AccessToken property of
    the
    AuthenticationResult object we received
    in the last step.
  2. We create a new SubscriptionClient (using
    our credentials) and call
    Subscriptions.ListAsync to
    get a list of subscriptions.
  3. We project that list of subscriptions into a new class called SubscriptionWrapper ,
    which is used with our ListBox – the
    SubscriptionWrapper class
    just overrides the
    ToString() method to provide
    a better description in our list box.

3) How to get a list of Logic Apps

Getting a list of LogicApps is simple too – all we need is our AccessToken ,
and the ID of a subscription.

We get the user to select a subscription from the list we obtained in the previous
step, and then execute this code:

using ( LogicManagementClient client
=
new LogicManagementClient (

new TokenCredentials (credentials.AccessToken)))

{

client.SubscriptionId = subscription.SubscriptionId;

Page Workflow >
workflowPage = client.Workflows.ListBySubscription();

List Workflow >
workflows = workflowPage.ToList();

}

Here, we’re doing the following:

  1. Creating a new LogicManagementClient ,
    passing it a new
    TokenCredentials instance
    (instantiated using our
    AccessToken ).
  2. Setting the SubscriptionId on the client
    to the ID of the subscription the user selected.
  3. Getting a list of LogicApps (known as
    Workflows in the SDK) by calling
    Workflows.ListBySubscription() .

So far, so good.

But if you were to try and execute the above code using the common tenant AccessToken we
obtained in Step 1) you may find yourself getting this error:

The full error text is:

“The access token is from the wrong issuer ‘https://sts.windows.net/
/’. It must match the tenant ‘https://sts.windows.net/
/’ associated with this subscription. Please use the authority (URL) ‘https://login.windows.net/
‘ to get the token. Note, if the subscription is transferred to another tenant there
is no impact to the services, but information about new tenant could take time to
propagate (up to an hour). If you just transferred your subscription and see this
error message, please try back later.”

The reason for this is simple: we authenticated against the common tenant, but
now we’re trying access data from a subscription which belongs to a separate tenant
– and we don’t have an
AccessToken for this
new tenant.

What we have to do in this case is acquire a new AccessToken (a
JWT) for the same user and client ID, but authorising against the tenant for the subscription
we selected.

i.e. we have an AccessToken , but it’s
a common tenant
AccessToken , and therefore
is limited in what is authorised: to work with resources that are specific to a particular
subscription, we now need an
AccessToken for
that specific subscription and tenant.

[Note: if we didn’t need to supply a list
of subscriptions, and new the subscription id and tenant id all along, we wouldn’t
even need to sign in using the common tenant].

To do this, we just need to run the code in Step 1) again, but this time instead
of using a
TenantId of “common”, we use the TenantId of
the subscription the User selected.

Running the code from Step 1) again will cause the login box to appear again,
but in most cases it will flash up blank, and then disappear, as long as it detects
that your common tenant credentials are valid for the new tenant.

Note: You may notice that when you use the portal, and
you log in, and then change your subscription, you don’t see a login box: that’s because
there’s a way to refresh your current
AccessToken for
the new tenant without going via the standard login route. I’ll cover this in a later
post.

4) How to logoff

Logging off should be really simple. But again, like most things in Azure when
you have multiple accounts, it’s not.

To log off for a given AccessToken (i.e.
a given login) you have to do the following:

  1. Clear the TokenCache.
  2. Clear any cookies created (e.g. if you ticked the “keep me logged in” option).
  3. Make a call to a logoff URL that looks like this: https://login.windows.net/{0}/oauth2/logout where
    {0} is the id of the tenant you’re logging off from.

The thing is, you can be logged into multiple tenants at the same time.

Personally, I think the logout functionality is a bit messy, although I suspect
it’s just that it needs to be surfaced properly in an API or REST call.

This is the code that needs to be executed. The AuthenticationObject needs
to be the one you created to login with (i.e. the one you put the
TenantId into).
The code for clearing the Cookies comes from the source code for the
UserTokenProvider in
GitHub (
https://github.com/Azure/azure-sdk-for-net/blob/master/src/Authentication/Common.Authentication/Authentication/UserTokenProvider.cs
hat tip to
Poul
K. Sørensen
for finding it first!):

string LogoutUrlBase = “https://login.windows.net/{0}/oauth2/logout” ;

authenticationContext.TokenCache.Clear();

new WebClient ().OpenRead( string .Format(LogoutUrlBase, “common” ));

NativeMethods .InternetSetOption( IntPtr .Zero, NativeMethods .INTERNET_OPTION_END_BROWSER_SESSION, IntPtr .Zero,
0);

private static class NativeMethods

{

internal const int INTERNET_OPTION_END_BROWSER_SESSION
= 42;

[ DllImport ( “wininet.dll” ,
SetLastError =
true )]

internal static extern bool InternetSetOption( IntPtr hInternet, int dwOption, IntPtr lpBuffer,

int lpdwBufferLength);

}

And that’s it.

Well, there’s a bit more: you should cache the AuthenticationObjects and AccessTokens for
each Tenant you login to so you don’t login more than once.

Here’s a link to the sample code – it’s a VS2015 project. It’s not production
quality code (hey, it’s a WinForm app! How retro!) but it does the job and gives you
an idea of how to do this yourself. Also, to keep the size down, I didn’t include
the NuGet packages, so when you first open it it will attempt to download them all
(or go to Tools/NuGet Packaage Manager/Manage NuGet Packages for Solution to download
them) – it won’t compile without them:

DemoGetSubscriptionsAndLogicApps
v1.0.zip (18.29 KB)

Any problems, let me know – I can’t guarantee it’ll work for all scenarios.

Sidebar

Here’s some useful information that should help you understand how
all of this works under the covers.

What “logging into Azure” means

When you log into Azure, you’re logging into Azure Active Directory
(AAD) – either one you host (in your subscription) or a common one (e.g. O365).

Technically, you’re using an AAD Tenant: A tenant is an instance
of Azure Active Directory just for your organisation (basically the same meaning when
we talk about
tenants in multi-threaded
programming): when you create an O365 Enterprise subscription you get assigned a tenant,
as you do when you create an Azure subscription [I’m simplifying this a great deal,
as it’s really a rather complex subject – see this post here
https://msdn.microsoft.com/en-us/library/azure/jj573650.aspx ,
or see Vittorio’s post here ,
or Brady’s post here for
more detail).

When you create a personal Azure Subscription, an AAD tenant is created
for you, and within that AAD is a default directory – that’s right, it’s an Azure
Active Directory Directory!

The default directory will use a name related to your Microsoft Account
Name e.g. if your Microsoft Account is dan@acme.com, your default directory will be
danacmecom.onmicrosoft.com .

You can create multiple AAD Directories from within the management
portal – an AAD Directory doesn’t belong to any subscription.

A subscription can only use one directory at a time – you can see
what directory is being used by going to the Settings view in the old portal.

Note : if you have an Enterprise O365
account, you can link the AAD tenant for your O365 account to your Azure subscription,
and then tell that subscription to use the O365 directory for authentication – which
means your corporate users can log in to the Azure subscription.

Because we’re using the management libraries, we’re using the OAuth
2.0
protocol for logging in – which means that when we successfully,
authenticate, we’re given a
Bearer token
in JWT form (pronounced “jot”), which is stored in a cookie locally against the id
of the client we authenticated against.

A JWT (JSON Web Token – see here for
more info, and for a decoder:
http://jwt.io/ )
is a string comprised of three Base64 encoded sections, separated by full stops/period
(.). You can decode these section to see what they contain – the important bits are
who has been authenticated, and when the JWT expires.

When we authenticate (using the Azure login window) we’re passed
one of these Bearer tokens, and we have to then transform it into a Credentials object
for use by the Management libraries.

Note that if you were using the REST interface (which is what the
Management libraries use underneath) you’d be passing the Bearer token in the HTTP
header for each request.

Two types of Azure Account

There are currently two types of Azure account:

  • Microsoft Accounts – these are the old
    Hotmail/Passport/Windows Live ID accounts. They’re a personal account, and they’re
    to be used to login to consumer-based Microsoft services e.g. Outlook.com, personal
    O365 subscriptions, Xbox Live etc. They’re also the default way of creating Azure
    subscriptions (until recently, you had to use a
    Microsoft
    Account
    to create an Azure Subscription, or as the Account
    Administrator). They’re denoted by the Microsoft four-colour flag icon.
  • Organisational Accounts (aka work or
    school accounts) – these are corporate/enterprise accounts, used to log into Enterprise
    services e.g. 0365 under an Enterprise License. They use the domain of your organisation,
    and you can sync these accounts with an on-premise AD server. They’re denoted by an
    icon that looks like a corporate photo id on a lanyard (see more here:
    https://azure.microsoft.com/en-gb/documentation/articles/sign-up-organization/ ).

Why is this important? Azure needs to know what type of account you
have, so it can work out which AAD Tenant to authenticate you against.

When you attempt to login to an Azure resource e.g. the management
portal, you’ll sometimes see this screen:

(it used to have a password box under it, and when you tabbed out
of the email address box, it would start doing something, but recently they’ve started
phasing out that version and started using the version above).

The reason this screen exists is to allow Azure to work out if you
have an
Org Account or a Microsoft
Account
– you’re then directed to the appropriate login screen: Org
Accounts
can customise their login screen.

If you happen to have an Org Account and
a
Microsoft Account with the
same name, then you’ll see this screen next:

This is Azure’s way of saying that it can’t work out which of the
two accounts you wish to use.

Some services only allow you to use a Microsoft
Account
to login – this will be obvious on the login screen:

The bane of those with multiple accounts: Auto Logon

One thing that those people with multiple Azure accounts (both Microsoft
Accounts
and Org Accounts )
hate with a passion (well, I do!!) is auto logon: you go a secured resource (e.g.
Azure Portal), click the
Sign In button
(or just type in the page URL), and instead of being asked which account you want
to use, you’re just logged in with what seems like a random account.

Why does this happen? It’s to do with how Azure AD has grown over
time, and how something that is designed to make life easier for people with single
accounts tends to make it more complex for those with multiple accounts.

Firstly, there’s the “Keep me signed in” feature: if you see this
checkbox on a login screen, and tick it, a cookie is created locally which stores
your authentication token.

Next time you go access a secured resource or click the SignIn button,
as long as the authentication token is still valid, then you’re automatically logged
in using that account.

But… if you then login using another account, and *don’t* tick the
same option, and then close your browser, and then try and login again, instead of
logging you in with the previously used account, you’re logged in with the original
account (i.e. the one you ticked the option for).

It gets more complex when you realise that whilst only a single Microsoft
Account
can be marked for auto-login at a single time, multiple
Corporate Accounts can be marked for auto-login at the same time.

I mention this because when it comes to logging in from code, you
can actually control this functionality and (hopefully) give your end user a more
pleasant login experience.

How to login to Azure if you don’t know the tenant

With all the discussion above, it may not be obvious on how to login
to Azure if you don’t know which AAD Tenant to authenticate against.

The solution is that Azure provides for the concept of a common tenant:
the common tenant indicates that you don’t know which tenant should be used (and you
don’t actually care): Azure will log you in against the relevant tenant for the user
signing in. After the user has logged in, you can get the tenant details (should you
need them).

Prompt Behavior

In Step 1, where we login, note the use of a variable called promptBehavior :
this lets us control whether or not we wish to see the login window if we’re already
logged in.

This value is an enum called PromptBehavior ,
and has the following values and meanings:

Member name

Description

Always

The user will be prompted for credentials even if there is a
token that meets the requirements already in the cache.

Auto

Acquire token will prompt the user for credentials only when
necessary. If a token that meets the requirements is already cached then the user
will not be prompted.

Never

The user will not be prompted for credentials. If prompting
is necessary then the AcquireToken request will fail.

RefreshSession

Re-authorizes (through displaying webview) the resource usage,
making sure that the resulting access token contains updated claims. If user logon
cookies are available, the user will not be asked for credentials again and the logon
dialog will dismiss automatically.

In our case, we want to use RefreshSession when
we first login (in case we have a token that can be reused) but want to set it to
Always when we Log off (so that we’re prompted to login next time we login). There
are some flaws in this plan: since we’re using the PowerShell client ID, if you logged
into PowerShell recently, and chosen the “keep me logged in” option, then when you
run this tool, you’ll be auto logged in as the user you logged in to PowerShell with.
The only way around this (that I know of at the moment) is to use a different client
id.

Back to Tech Blog