How to call Azure Rest API in C# - c#

I'm new to C# world. I have a project where I need to collect Azure compute usage quotas across all regions from 700+ subscriptions. I have done it easily using PowerShell (Get-AzVMUsage).
I have to do it using C#. I guess I need to use Rest API for it. (I am open to another way to achieve this).
Azure Rest API: GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Compute/locations/{location}/usages?api-version=2019-12-01
How can I fetch results using the above Rest API? Once I get results from this Rest API, I can put my business logic on top of it to perform data aggregations and loop it through 700+ Subscriptions and dump the data in SQL-MI.

I Google'ed and figured out the way from below url.
https://learn.microsoft.com/en-us/archive/blogs/benjaminperkins/how-to-securely-connect-to-azure-from-c-and-run-rest-apisMSDN Forum
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json;
namespace AzureCapacityUsage
{
class Program
{
static async Task Main()
{
try
{
string token = await GetAccessToken(TenantID,ClientID,Password);
await GetResults(token);
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
}
private static async Task<string> GetResults(string token)
{
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://management.azure.com/subscriptions/")
};
string URI = $"{SubscriptionGUID}/providers/Microsoft.Compute/locations/{Region}/usages?api-version=2019-12-01";
httpClient.DefaultRequestHeaders.Remove("Authorization");
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
HttpResponseMessage response = await httpClient.GetAsync(URI);
var HttpsResponse = await response.Content.ReadAsStringAsync();
var JSONObject = JsonConvert.DeserializeObject<object>(HttpsResponse);
Console.WriteLine(JSONObject);
var JSONObj = new JavaScriptSerializer().Deserialize<Dictionary<string, string>>(JSONObject);
return response.StatusCode.ToString();
}
private static async Task<string> GetAccessToken(string tenantId, string clientId, string clientKey)
{
Console.WriteLine("Begin GetAccessToken");
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId, clientKey);
var result = await authenticationContext
.AcquireTokenAsync("https://management.azure.com/", credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
}
}

The answer from Vinny is longer supported. As of December 2022 Microsoft.IdentityModel.Clients.ActiveDirectory Nuget which is used with AuthenticationContext is no longer supported.
We can use the Azure.Identity Nuget and then replace the GetAccessToken method with this...
private static async Task<string> GetAccessToken(string tenantId, string clientId, string clientKey)
{
Console.WriteLine("Begin GetAccessToken");
var credentials = new ClientSecretCredential(tenantId, clientId, clientKey);
var result = await credentials.GetTokenAsync(new TokenRequestContext(new[] { "https://management.azure.com/.default" }), CancellationToken.None);
return result.Token;
}
That said it may be easier to use the SDKs. I have written a blog post on both the SDKs and Rest APIs that you may find useful.

System.Net.HttpClient is your friend here :
using System.Net.Http;
using System.Threading.Tasks;
namespace Sandbox
{
public class SampleCall
{
static async Task<string> CallApi()
{
var subscriptionId = "subscriptionIdHere";
var location = "locationHere";
var uri = $"https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Compute/locations/{location}/usages?api-version=2019-12-01";
using var client = new HttpClient();
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return string.Empty;
}
}
}
Usage :
var content = await SampleCall.CallApi();

Related

Microsoft Graph API authentication error(Error Description: /me request is only valid with delegated authentication flow)

We have a console application that uses Aspose email dll for sending mails and various other functionalities.
Since it uses basic authentication that is getting deprecated soon by microsoft so we are planning to have authentication using Microsoft Graph API.
So I have done below steps
1)Registered the app in Microsoft Azure active directory and gave permissions under Microsoft Graph. PFA(AzureAPP_permissions.png)
2)I added code for generating token using graph API and then tried fetching email,sending email using Aspose dll but every time it throws below error.
**Aspose.Email.AsposeBadServerResponceException: 'Server error Status: BadRequest
Description: /me request is only valid with delegated authentication flow.
Details:
POST: https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messages
---------------------------------------**
Please help what is going wrong here
PFB the code part
Graph_code
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Aspose.Email.Clients;
using Aspose.Email.Clients.Graph;
using Aspose.Email.Mapi;
using EASendMail;
namespace Email
{
internal class Graph_API
{
private static string _clientId = ConfigurationManager.AppSettings["ClientId"];
private static string _tenantId = ConfigurationManager.AppSettings["TenantId"];
private static string _secretValue = ConfigurationManager.AppSettings["SecretValue"];
static string _postString(string uri, string requestData)
{
HttpWebRequest httpRequest = WebRequest.Create(uri) as HttpWebRequest;
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded";
using (Stream requestStream = httpRequest.GetRequestStream())
{
byte[] requestBuffer = Encoding.UTF8.GetBytes(requestData);
requestStream.Write(requestBuffer, 0, requestBuffer.Length);
requestStream.Close();
}
try
{
HttpWebResponse httpResponse = httpRequest.GetResponse() as HttpWebResponse;
var responseText = new StreamReader(httpResponse.GetResponseStream()).ReadToEnd();
Console.WriteLine(responseText);
return responseText;
}
catch (WebException ep)
{
if (ep.Status == WebExceptionStatus.ProtocolError)
{
var responseText = new StreamReader(ep.Response.GetResponseStream()).ReadToEnd();
Console.WriteLine(responseText);
}
throw ep;
}
}
public string GenerateToken()
{
string client_id = _clientId;
string client_secret = _secretValue;
string tenant = _tenantId;
string requestData =
string.Format("client_id={0}&client_secret={1}" + "&scope=https://graph.microsoft.com/.default&grant_type=client_credentials",
client_id, client_secret);
string tokenUri = string.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", tenant);
string responseText = _postString(tokenUri, requestData);
OAuthResponseParser parser = new OAuthResponseParser();
parser.Load(responseText);
var vv = parser.AccessToken;
return vv;
}
public void Generatemail()
{
interface_class bb = new interface_class();
IGraphClient client = GraphClient.GetClient(bb, _tenantId);
MapiMessage mm = new MapiMessage();
mm.Subject = "EMAILNET-39318 " + Guid.NewGuid().ToString();
mm.Body = "EMAILNET-39318 REST API v1.0 - Create Message";
mm.SetProperty(KnownPropertyList.DisplayTo, "xxx#outlook.com");
mm.SetProperty(KnownPropertyList.SenderName, "xxx#outlook.com");
mm.SetProperty(KnownPropertyList.SentRepresentingEmailAddress, "xxx#outlook.com");
// Create message in inbox folder
MapiMessage createdMessage = client.CreateMessage(Aspose.Email.Clients.Graph.KnownFolders.Inbox, mm);
}
public void FetchMail()
{
try
{
interface_class bb = new interface_class();
using (IGraphClient client = GraphClient.GetClient(bb, _tenantId))
{
FolderInfoCollection folderInfoCol1 = client.ListFolders();
FolderInfo inbox = null;
foreach (FolderInfo folderInfo in folderInfoCol1)
{
if (folderInfo.DisplayName.Equals("Inbox", StringComparison.InvariantCultureIgnoreCase))
{
inbox = folderInfo;
break;
}
}
MessageInfoCollection messageInfoCol = client.ListMessages(inbox.ItemId);
MessageInfo messageInfo = messageInfoCol[0];
MapiMessage message = client.FetchMessage(messageInfo.ItemId);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
Itokenimplementor_class code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Aspose.Email.Clients;
using Aspose.Email.Clients.Graph;
namespace Email
{
internal class interface_class : ITokenProvider
{
Graph_API obj = new Graph_API();
DateTime expirationDate = DateTime.Today.AddDays(1);
public void Dispose()
{
throw new NotImplementedException();
}
public OAuthToken GetAccessToken()
{
string token = obj.GenerateToken();
return new OAuthToken(token, expirationDate);
}
public OAuthToken GetAccessToken(bool ignoreExistingToken)
{
throw new NotImplementedException();
}
}
}
https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messages This endpoint is used to query inbox messages for a user right? Then who is /me here? And this is the issue.
In your code snippet, I noticed that you set the grant_type=client_credentials so you are using client credential flow. It's correct but pls note that you need to give application api permission. Application api permission is for client credential flow. And this api require Mail.ReadBasic.All, Mail.Read, Mail.ReadWrite permissions.
If you can generate access token successfully, you may decode the token the check if the roles claim contained correct permissions.
When you get a correct token, then pls use Get: /users/{id | userPrincipalName}/mailFolders/inbox/messages instead of /me. If you want to query emails for user1#yourtenant.onmicrosoft.com, then the url can be /users/user1#yourtenant.onmicrosoft.com/mailFolders/inbox/messages.
I also notice that you had GraphClient in your code. So you may also try my code below:
using Azure.Identity;
using Microsoft.Graph;
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_app_client_id";
var clientSecret = "client_secret_for_the_azuread_app";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var messages = await graphClient.Users["user_id/PrincipalName"].Messages
.Request()
.GetAsync();

How can I query Github repository and issues using GraphQL and C#?

I want to query GitHub repository and issues using GraphQL and C#. When I query GitHub repository and issues, VS Code keeps erroring out error CS1003: Syntax error, ',' expected. If I replace the repository query with a simple query(viewer{login}), it works fine.
using GraphQL;
using Newtonsoft.Json;
using System;
using System.Text;
using System.Net.Http.Headers;
class Program
{
static async Task Main(string[] args)
{
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://api.github.com/graphql")
};
httpClient.DefaultRequestHeaders.Add("User-Agent", "MyConsoleApp");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "MyPersonalToken");
var queryObject = new
{
query = #"
query {
repository(owner:"BT23", name:"demo-repo"){
issues(last:10){
edges{
node{
title
}
}
}
}
}",
variables = new { }
};
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
Content = new StringContent(JsonConvert.SerializeObject(queryObject), Encoding.UTF8, "application/json")
};
dynamic responseObj;
using (var response = await httpClient.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
responseObj = JsonConvert.DeserializeObject<dynamic>(responseString);
}
//Console.WriteLine(responseObj.data.viewer.login);
//Console.ReadLine();
}
}
Please advise.

C# Firebase Admin SDK Push Notifications

I want to send push notifications to a mobile app, so I installed Firebase Admin SDK to my .Net project.
I followed the instructions on Firebase docs, but when I call the SendAsync from the SDK, it just hangs. Any idea why it would hang? Am I missing steps?
Here is my API code (Note this is only demo code to get it to work):
public async void SendPushNotification()
{
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromFile("path to json file"),
});
var registrationToken = "fqCo_-tXKvY:APA91bGQ47Q2egnqn4Ml...";
var message = new FirebaseAdmin.Messaging.Message()
{
Token = registrationToken,
};
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
Console.WriteLine("Successfully sent message: " + response);
}
you have a deadlock because SendAsync will never finish.
if you are calling from ui thread you need to consider using ConfigureAwait(false)
so your snippet will look like below:
public async void SendPushNotification()
{
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromFile("path to json file"),
});
var registrationToken = "fqCo_-tXKvY:APA91bGQ47Q2egnqn4Ml...";
var message = new FirebaseAdmin.Messaging.Message()
{
Token = registrationToken,
};
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message).ConfigureAwait(false);
Console.WriteLine("Successfully sent message: " + response);
}
The firebaseAdmin messaging throw a token error with the .net libraries hence i tested with Postman and works i was able to send the notifications and I see postman generates a code for Resharper library and was easy to install and use, is better to serialize the message with Json and send as parameter
using Firebase.Auth;
using FirebaseAdmin;
using FirebaseAdmin.Auth;
using Google.Apis.Auth.OAuth2;
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using FirebaseAuth = FirebaseAdmin.Auth.FirebaseAuth;
using FirebaseAdmin.Messaging;
using RestSharp;
using Newtonsoft.Json;
namespace messaging
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
string token = "dtPMeTshqr??????????????";
var data = new
{
to = token,
notification = new
{
body = "Nueva notificacion de prueba",
title = "Notificacion de prueba",
},
priority = "high"
};
var json = JsonConvert.SerializeObject(data);
Sendmessage(json);
}
private static string Sendmessage(string json)
{
var client = new RestClient("https://fcm.googleapis.com/fcm/send");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "key=AAAAexjc2Qg:APx??????????????");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", json, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
return response.Content;
}
}
}

Microsoft AnalysisServices - start and stop server using Azure Functions

I wanted to write an Azure Function to stop and resume MS Analysis Sevice.
I have been tasked to do this using API Resume and API Suspend.
It's my first time with C# (I have Python/Javascript background).
What am I doing wrong? Can someone provide correct solution? Would be grateful from the bottom of my heart.
Here is my current code sample:
using System;
using System.Net;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
static HttpClient client = new HttpClient();
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req)
{
var subscriptionId = "mySId";
var resourceGroupName = "myRGName";
var serverName = "mySName";
var APIurl = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.AnalysisServices/servers/{serverName}/resume?api-version=2017-08-01";
var response = await client.PostAsync(APIurl, null);
string result = await response.Content.ReadAsStringAsync();
return req.CreateResponse(HttpStatusCode.OK);
}
This is what I get in console. Server is not stopping though.
As the api "Resume" you mentioned in your question requires Azure Active Directory OAuth2 Flow, so you need to get the access token first by referring to this tutorial and then use the access token to request this api in your function code.
In the code of your function app, you need to set the access token which we get above as the value of Authorization and then do the post reuqest.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");
For those in times of need - this is my complete working solution.
using System;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http.Headers;
using System.Net;
using System.Net.Http;
using System.Collections.Generic;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace ManageAS
{
public class ManageAS
{
string accessToken = null;
string responseString = null;
string serverStatus = null;
string serverFullURI = Environment.GetEnvironmentVariable("FULL_SERVER_URI");
static string shortServerName = Environment.GetEnvironmentVariable("SHORT_SERVER_NAME");
string resourceURI = "https://management.core.windows.net/";
string clientID = Environment.GetEnvironmentVariable("CLIENT_ID");
string clientSecret = Environment.GetEnvironmentVariable("CLIENT_SECRET");
string tenantID = Environment.GetEnvironmentVariable("TENANT_ID");
static string resourceGroupName = Environment.GetEnvironmentVariable("RESOURCE_GROUP_NAME");
static string subscriptionId = Environment.GetEnvironmentVariable("SUBSCRIPTION_ID");
static Uri apiURI = null;
[FunctionName("PerformAction")]
public async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestMessage req,
ILogger log)
{
await GetAccessToken();
await GetServerStatus(accessToken);
if (serverStatus == "Paused")
{
apiURI = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}/resume?api-version=2017-08-01", subscriptionId, resourceGroupName, shortServerName));
this.responseString = await ServerManagement(apiURI, accessToken);
}
if (serverStatus == "Succeeded")
{
apiURI = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}/suspend?api-version=2017-08-01", subscriptionId, resourceGroupName, shortServerName));
this.responseString = await ServerManagement(apiURI, accessToken);
}
return req.CreateResponse(HttpStatusCode.OK, responseString);
}
protected async Task<string> GetAccessToken()
{
string authority = "https://login.microsoftonline.com/" + tenantID;
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
var token = await authenticationContext.AcquireTokenAsync(resourceURI, new ClientCredential(clientID, clientSecret));
accessToken = token.AccessToken;
return accessToken;
}
protected async Task<string> ServerManagement(Uri url, string token)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await client.PostAsync(url.ToString(), null);
response.EnsureSuccessStatusCode();
HttpContent content = response.Content;
string json;
json = await content.ReadAsStringAsync();
return json;
}
protected async Task<string> GetServerStatus(string token)
{
var url = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}?api-version=2017-08-01", subscriptionId, resourceGroupName, shortServerName));
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await client.GetAsync(url.ToString());
response.EnsureSuccessStatusCode();
string sJson = await response.Content.ReadAsStringAsync();
var dictResult = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(sJson);
if (!dictResult.ContainsKey("properties")) return null;
var dictProperties = dictResult["properties"] as Newtonsoft.Json.Linq.JObject;
if (dictProperties == null || !dictProperties.ContainsKey("state")) return null;
serverStatus = Convert.ToString(dictProperties["state"]);
return serverStatus;
}
}
}

How do I delete an AppRoleAssignment using the Azure Active Directory .NET SDK?

I'm trying to figure out how to delete an AppRoleAssignment from either an Group or a User using the Graph API for Azure Active Directory. I'm using the .NET SDK (Microsoft.Azure.ActiveDirectory.GraphClient).
I've tried using the standard DeleteAsync method that's on every IEntityBase, but it fails with an error. It's issuing an HTTP request that looks like this:
DELETE /{tenantId}/directoryObjects/{appRoleAssignment ObjectID}/Microsoft.DirectoryServices.AppRoleAssignment?api-version=1.5
which fails with a 400 Bad Request with the error "Direct queries to this resource type are not supported."
This isn't the correct way to delete AppRoleAssignments using the Graph API according to this Microsoft blog post which says you need to do an HTTP request that looks like:
DELETE /{tenantId}/users/{user object ID}/appRoleAssignments/{appRoleAs}?api-version=1.5
If I do a manual HTTP request using HttpClient using that URL format, it works, but I want to know how to do this within the bounds of the .NET library rather than doing manual HTTP requests myself.
How do I delete AppRoleAssignments via the .NET library?
While it is not fixed, you can make a manual HTTP-request, but still using Azure AD SDK to acqure the token. Something like this:
var tenantId = "<guid> tenant id";
var appId = "<guid> your Azure app id";
var appKey = "your app key";
var authority = "i.e. https://login.windows.net/mycompany.onmicrosoft.com";
var graphUrl = "https://graph.windows.net/";
public async Task RemoveRoleFromUser(Guid userId, string roleObjectId) {
var uri = string.Format("{0}/users/{1}/appRoleAssignments/{2}?api-version=1.5", tenantId, userId, roleObjectId);
await ExecuteRequest<object>(uri, HttpMethod.Delete);
}
private async Task<T> ExecuteRequest<T>(string uri, HttpMethod method = null, Object body = null) where T : class {
if (method == null) method = HttpMethod.Get;
T response;
var token = await AcquireTokenAsyncForApplication();
using (var httpClient = new HttpClient { BaseAddress = getServicePointUri() }) {
var request = new HttpRequestMessage(method, uri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
if (body != null) {
request.Content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json");
}
var responseMessage = await httpClient.SendAsync(request).ConfigureAwait(false);
responseMessage.EnsureSuccessStatusCode();
response = await responseMessage.Content.ReadAsAsync<T>();
}
return response;
}
private async Task<string> AcquireTokenAsyncForApplication() {
ClientCredential clientCred = new ClientCredential(appId, appKey);
var authenticationContext = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(graphUrl, clientCred);
return authenticationResult.AccessToken;
}
private Uri getServicePointUri() {
Uri servicePointUri = new Uri(graphUrl);
Uri serviceRoot = new Uri(servicePointUri, tenantId);
return serviceRoot;
}
ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
user = (User) await client.Users.GetByObjectId(objectId).ExecuteAsync();
var roleId = "";
await user.AppRoleAssignments.Where(t=>t.ObjectId==roleId).FirstOrDefault().DeleteAsync();
The following websites might be helpful:
https://github.com/AzureADSamples/WebApp-RoleClaims-DotNet
https://github.com/AzureADSamples/WebApp-GraphAPI-DotNet

Categories