My company is looking into reporting on Azure. We only want our customers to give us read only credentials for us to use. I did some research and it looks like Azure Active Directory does just that. So I'm looking to authenticate using a read only Azure Directory Application.
To get me started I was following this blog on using the Management API via Azure Active Directory.
https://msdn.microsoft.com/en-us/library/azure/dn722415.aspx
Aside from the approach show being very unfriendly, it doesn't work =(
I get this error after logging in as a global administrator:
"AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'."
Did some research and found this style of authentication was for native app and NOT web apps (despite what the blog post saying other wise..). So I made a tweak. My GetAuthorizationHeader now looks like this:
private static string GetAuthorizationHeader()
{
AuthenticationResult result = null;
var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);
string clientId = ConfigurationManager.AppSettings["clientId"];
string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
var thread = new Thread(() =>
{
result = context.AcquireToken(
"https://management.core.windows.net/",
clientCred);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Name = "AquireTokenThread";
thread.Start();
thread.Join();
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
I am able to get the Access Token (yay). But now when I try to use this with the Azure Management library client I get this error:
"ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription."
I double checked my permissions in my application. It looked good. I tried giving full access to everything to see if that would have made a difference.
I double checked my tenantId, clientId, and subscriptionId, all looked good.
I made sure the subscription I'm using is pointed to the AD my application is in.
I tried making a new secret key.
My guess is this is the issue:
However in this UI I am unable to select any values for that property. I'm unsure if this is the result of a bug or an unfinished feature.
Am I missing something here?
Thanks
Here's my full code for reference:
class Program
{
static void Main(string[] args)
{
var token = GetAuthorizationHeader();
var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"], token);
using (var computeClient = new ComputeManagementClient(credential))
{
var images = computeClient.VirtualMachineOSImages.List();
}
}
private static string GetAuthorizationHeader()
{
AuthenticationResult result = null;
var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);
string clientId = ConfigurationManager.AppSettings["clientId"];
string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
var thread = new Thread(() =>
{
result = context.AcquireToken(
"https://management.core.windows.net/",
clientCred);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Name = "AquireTokenThread";
thread.Start();
thread.Join();
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
}
EDIT: Progress has been made. As I discussed with Gaurav, I needed to ditch the Azure Management Library because as of right now it does not seem to support Azure Resource Manager (ARM) API! So instead I did raw web requests. And it works as intended. If I remove role access off my AD Application I get access denied. When I have it I get back data.
One thing I'm not sure about is making it so my application is auto-adding to new resources.
Also, Is there a way to list Resource Groups that are accessible for my AD Application?
New code:
class Program
{
static void Main(string[] args)
{
var token = GetAuthorizationHeader();
string subscriptionId = ConfigurationManager.AppSettings["subscriptionId"];
string resourceGroupName = ConfigurationManager.AppSettings["resourceGroupName"];
var uriListMachines = string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualmachines?api-version=2015-05-01-preview", subscriptionId, resourceGroupName);
var t = WebRequest.Create(uriListMachines);
t.ContentType = "application/json";
t.Headers.Add("Authorization", "Bearer " + token);
var response = (HttpWebResponse)t.GetResponse();
string result = "";
using (var reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
//Original Attempt:
//var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"], token);
//using (var client = CloudContext.Clients.CreateComputeManagementClient(credential))
//{
// var images = client.VirtualMachineVMImages.List();
//}
}
private static string GetAuthorizationHeader()
{
AuthenticationResult result = null;
var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);
string clientId = ConfigurationManager.AppSettings["clientId"];
string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
var thread = new Thread(() =>
{
result = context.AcquireToken(
"https://management.core.windows.net/",
clientCred);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Name = "AquireTokenThread";
thread.Start();
thread.Join();
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
}
EDIT EDIT: I figured out my hung up. Resources created in the OLD portal will get it's own distinct resource group.
From what I can tell you can not add a resource made in the old portal existing resource group (boooo). Resources created in the new portal will be able to assign the resource to an existing group (aka one that gives a role access to my AD Application).
This is such a mess! But at least I know what is going on now.