Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create ServiceClientCredential to be used with Microsoft.Azure.Management.Compute

I am trying to programmatically retrieve the HostedServices from Microsoft.Azure.Management.Compute using C#. This requires ServiceClientCredential and I do not know how to get it.

How can I instantiate this class?

I am able to get them using Microsoft.WindowsAzure.Management.Compute but here it returns only the instances under ResourceManager not the classic instances.

like image 712
user5748653 Avatar asked Feb 05 '16 15:02

user5748653


People also ask

Which package is used for authentication purpose in Azure?

The azure. identity package in the Azure SDK manages tokens for you behind the scenes. This makes using token based authentication as easy to use as a connection string.

What is Defaultazurecredential?

Provides a default TokenCredential authentication flow for applications that will be deployed to Azure. The following credential types if enabled will be tried, in order: EnvironmentCredential. ManagedIdentityCredential. SharedTokenCacheCredential.


3 Answers

First you need to create Active Directory application. See How to: Use the portal to create an Azure AD application and service principal that can access resources

The sample code below uses the nuget package Microsoft.Azure.Management.Compute 13.0.1-prerelease:

public class CustomLoginCredentials : ServiceClientCredentials
{
    private string AuthenticationToken { get; set; }
    public override void InitializeServiceClient<T>(ServiceClient<T> client)
    {
        var authenticationContext = new AuthenticationContext("https://login.windows.net/{tenantID}");
        var credential = new ClientCredential(clientId: "xxxxx-xxxx-xx-xxxx-xxx", clientSecret: "{clientSecret}");

        var result = authenticationContext.AcquireToken(resource: "https://management.core.windows.net/", clientCredential: credential);

        if (result == null) throw new InvalidOperationException("Failed to obtain the JWT token");

        AuthenticationToken = result.AccessToken;
    }
    public override async Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request == null) throw new ArgumentNullException("request");

        if (AuthenticationToken == null) throw new InvalidOperationException("Token Provider Cannot Be Null");
        
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AuthenticationToken);
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        //request.Version = new Version(apiVersion);
        await base.ProcessHttpRequestAsync(request, cancellationToken);
    }
}

Then you can initialize the client like this:

netClient = new Microsoft.Azure.Management.Compute.ComputeManagementClient(new CustomLoginCredentials());
netClient.SubscriptionId = _subscriptionId;
like image 83
Ahmed Ashmawy Avatar answered Oct 21 '22 11:10

Ahmed Ashmawy


The way you'd do this now is to use ITokenProvider and Microsoft.Rest.TokenCredentials.

public class CustomTokenProvider : ITokenProvider
{
    private readonly CustomConfiguration _config;

    public CustomTokenProvider(CustomConfiguration config)
    {
        _config = config;
    }

    public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(CancellationToken cancellationToken)
    {
        // For app only authentication, we need the specific tenant id in the authority url
        var tenantSpecificUrl = $"https://login.microsoftonline.com/{_config.TenantId}/";

        // Create a confidential client to authorize the app with the AAD app
        IConfidentialClientApplication clientApp = ConfidentialClientApplicationBuilder
                                                                        .Create(_config.ClientId)
                                                                        .WithClientSecret(_config.ClientSecret)
                                                                        .WithAuthority(tenantSpecificUrl)
                                                                        .Build();
        // Make a client call if Access token is not available in cache
        var authenticationResult = await clientApp
            .AcquireTokenForClient(new List<string> { _config.Scope })
            .ExecuteAsync();


        return new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
    }
}

And then in your DI configuration

services.AddTransient<IPowerBIClient, PowerBIClient>((provider) =>
{
    var config = provider.GetRequiredService<CustomConfiguration>();
    var tokenProvider = provider.GetRequiredService<CustomTokenProvider>();

    return new PowerBIClient(new Uri(config.BaseUrl), new TokenCredentials(tokenProvider));
});

My example is used with Power BI but would work with anything that needs access to ServiceClientCredentials.

You can use the Nuget package Microsoft.Identity.Client for IConfidentialClientApplication.

like image 31
antdev Avatar answered Oct 21 '22 11:10

antdev


A bit later in the game, but this is how we do this in our project. We use the token credentials that is provided by the .net framework to access a managed identity, or visual studio (code) identity, or interactive. And connect to the azure infrastructure API.

internal class CustomTokenProvider : ServiceClientCredentials
{
    private const string BearerTokenType = "Bearer";
    private TokenCredential _tokenCredential;
    private readonly string[] _scopes;
    private readonly IMemoryCache _cache;

    public CustomTokenProvider(TokenCredential tokenCredential, string[] scopes, IMemoryCache cache)
    {
        _tokenCredential = tokenCredential ?? throw new ArgumentNullException(nameof(tokenCredential));
        _scopes = scopes ?? throw new ArgumentNullException(nameof(scopes));
        _cache = cache;
    }

    public override async Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request == null)
        {
            throw new ArgumentNullException(nameof(request));
        }

        var token = await _cache.GetOrCreateAsync("accessToken-tokenProvider." + string.Join("#", _scopes), async e =>
        {
            var accessToken = await _tokenCredential.GetTokenAsync(new TokenRequestContext(_scopes), cancellationToken);
            e.AbsoluteExpiration = accessToken.ExpiresOn;
            return accessToken.Token;
        });
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(BearerTokenType, token);
        await base.ProcessHttpRequestAsync(request, cancellationToken).ConfigureAwait(false);
    }
}

Couple of remarks:

  • The TokenCredential class does not cache tokens and if you don't do it, it will trigger an error at azure due to excessive requests.
  • Calling a v1 endpoint with v2 calls requires to be a bit creative in the scopes. So when you need to access the management API, provide the following scope "https://management.core.windows.net/.default" and not the user_impersonate scope as specified. This due to some internal conversion on the different endpoints. And '.default' scope is always available and will give yout the on
like image 3
verbedr Avatar answered Oct 21 '22 10:10

verbedr