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;
}
}
}
Related
I am currently trying to make an api call in c# using a MultiPartFormDataContent but I keep
getting the following error:
"Response: {"statusCode":400,"error":"Bad Request","message":""Image file" must be of type object","validation":{"source":"payload","keys":["images"]}}"
This is my Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace Example
{
public class Test
{
public static void Main(string[] args)
{
Task<string> test = testFunction();
Console.WriteLine("Result: " + test.Result);
}
public static async Task<string> testFunction()
{
const string file = "C:\\ExamplePath\\image_1.jpeg";
const string URL = "https://example-api?api-key=example-key";
string boundary = "---8d0f01e6b3b5dafaaadaad";
MultipartFormDataContent multipartContent = new MultipartFormDataContent(boundary);
var streamContent = new StreamContent(File.Open(file, FileMode.Open));
var stringContent = new StringContent("flower");
multipartContent.Add(stringContent, "organs");
multipartContent.Add(streamContent, "images");
try
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.PostAsync(URL, multipartContent);
Console.WriteLine("Response: " + await response.Content.ReadAsStringAsync());
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine("IN METHIOD: " + content);
return content;
}
return null;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return null;
}
}
}
}
It's obviously a problem with how I am trying to do the api call but I don't know how to do it with an object instead like mentioned the error message.
This link has some good examples and where I actually got my code snippet from below.
Here's a basic example of using MultiFormDataContent:
HttpClient httpClient = new HttpClient();
MultipartFormDataContent form = new MultipartFormDataContent();
form.Add(new StringContent(username), "username");
form.Add(new StringContent(useremail), "email");
form.Add(new StringContent(password), "password");
form.Add(new ByteArrayContent(file_bytes, 0, file_bytes.Length), "profile_pic", "hello1.jpg");
HttpResponseMessage response = await httpClient.PostAsync("PostUrl", form);
response.EnsureSuccessStatusCode();
httpClient.Dispose();
string sd = response.Content.ReadAsStringAsync().Result;
I hope this helps or points you in the right direction.
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();
Currently I have to async methods Post() & Get(). For my Post() method is returning a access token and if you look at the bottom of my post method I am calling my Get() in there also, for the simple reason of being able to call string result in my get. but even with a successful access token I Keep getting a 401 unauthorized Status code, why?
Click to view error in VS
namespace APICredential.Controllers
{
[RoutePrefix("api")]
public class ValuesController : ApiController
{
[HttpGet, Route("values")]
public async Task<string> Post()
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.elliemae.com/oauth2/");
var parameters = new Dictionary<string, string>()
{
{"grant_type", "password"}, //Gran_type Identified here
{"username", "admin#encompass:BE11200822"},
{"password", "Shmmar****"},
{"client_id", "gpq4sdh"},
{"client_secret", "dcZ42Ps0lyU0XRgpDyg0yXxxXVm9#A5Z4ICK3NUN&DgzR7G2tCOW6VC#HVoZPBwU"},
{"scope", "lp"}
};
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "v1/token")
{
Content = new FormUrlEncodedContent(parameters)
};
HttpResponseMessage response = await client.SendAsync(request);
string resulted = await response.Content.ReadAsStringAsync();
await Get(resulted);
return resulted;
}
}
[HttpGet, Route("values/get")]
public async Task<string> Get(string resulted)
{
string res = "";
using (var client = new HttpClient())
{
// HTTP POST
client.BaseAddress = new Uri("https://api.elliemae.com/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync("/encompass/v1/loans/{ea7c29a6-ee08-4816-99d2-fbcc7d15731d}?Authorization=Bearer "+resulted+"&Content-Type=application/json").Result;
using (HttpContent content = response.Content)
{
// ... Read the string.
Task<string> result = content.ReadAsStringAsync();
res = result.Result;
}
}
return res;
}
The following is missing:
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + Accesstoken);
This is your default header once you insert this then you have premission to getstring data from whatever your URL is...
code will look like this:
public async Task<string> Get(string Accesstoken)
{
string res = "";
using (var client = new HttpClient())
{
Accesstoken = Accesstoken.Substring(17, 28);
client.BaseAddress = new Uri("https://api.elliemae.com/");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + Accesstoken);
var response = client.GetAsync("encompass/v1/loans/ea7c29a6-ee08-4816-99d2-fbcc7d15731d").Result;
using (HttpContent content = response.Content)
{
// ... Read the string.
Task<string> result = content.ReadAsStringAsync();
res = result.Result;
}
I’m working with a project that has been created as a ASP.Net Web Application with the ‘Web API’ template and ‘Individual User Accounts’ enabled as the authentication option. I have a console application that consumes the web api. But When I want to get the token it gives me a complete html string with "404 not found" in stead of a json array. What am I doing wrong?
This is mij console app code:
using ConsoleApplication1.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
const string userName = "user#user.com";
const string password = "Password01!";
const string apiBaseUri = "http://localhost/WebAPITest";
const string apiGetPeoplePath = "/api/people";
static void Main(string[] args)
{
//Get the token
var token = GetAPIToken(userName, password, apiBaseUri).Result;
Console.WriteLine("Token: {0}", token);
//Make the call
var response = GetRequest(token, apiBaseUri, apiGetPeoplePath).Result;
Console.WriteLine("response: {0}", response);
//wait for key press to exit
Console.ReadKey();
}
private static async Task<string> GetAPIToken(string userName, string password, string apiBaseUri)
{
using (var client = new HttpClient())
{
//setup client
client.BaseAddress = new Uri(apiBaseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//setup login data
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", userName),
new KeyValuePair<string, string>("password", password),
});
//send request
HttpResponseMessage responseMessage = await client.PostAsync("/Token", formContent);
//get access token from response body
var responseJson = await responseMessage.Content.ReadAsStringAsync();
var jObject = JObject.Parse(responseJson);
return jObject.GetValue("access_token").ToString();
}
}
static async Task<string> GetRequest(string token, string apiBaseUri, string requestPath)
{
using (var client = new HttpClient())
{
//setup client
client.BaseAddress = new Uri(apiBaseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
//make request
HttpResponseMessage response = await client.GetAsync(requestPath);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
}
}
}
This is my Startup.Auth:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
After a couple of days thinking and trying. I found a solution that worked for me. In my previous code it worked only in iisexpress (I don't know why so if someone knows please share!). But in my new code it worked for both iisexpress and iis 7.5.
using ConsoleApplication1.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
const string userName = "user#user.com";
const string password = "Password01!";
const string apiBaseUri = "http://localhost/WebAPITest";
private static async void GetAPIToken()
{
string responseJson = await ResponseAsStringAsync(string.Format("{0}/token", apiBaseUri),
new[]
{
new KeyValuePair<string, string>("password", password),
new KeyValuePair<string, string>("username", userName),
new KeyValuePair<string, string>("grant_type", "password"),
});
var jObject = JObject.Parse(responseJson);
string token = jObject.GetValue("access_token").ToString();
}
public static async Task<string> ResponseAsStringAsync(string url, IEnumerable<KeyValuePair<string, string>> postData)
{
string responseString = string.Empty;
var uri = new Uri(url);
using (var client = new HttpClient())
using (var content = new FormUrlEncodedContent(postData))
{
content.Headers.Clear();
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
HttpResponseMessage response = await client.PostAsync(uri, content);
responseString = await response.Content.ReadAsStringAsync();
}
return responseString;
}
static void Main(string[] args)
{
GetAPIToken();
Console.ReadKey();
}
}
}
I hope it helps someone else with the same problem.
I have some file to upload and some of the files failed because the post is asynchronous and not synchronous..
I'm trying to make this call as synchronized call..
I want to wait for the response.
How can I make this call as synchronous?
static async Task<JObect> Upload(string key, string url, string
sourceFile, string targetFormat)
{
using (HttpClientHandler handler = new HttpClientHandler {
Credentials = new NetworkCredential(key, "")
})
using (HttpClient client = new HttpClient(handler))
{
var request = new MultipartFormDataContent();
request.Add(new StringContent(targetFormat), "target_format");
request.Add(new StreamContent(File.OpenRead(sourceFile)),
"source_file",
new FileInfo(sourceFile).Name);
using (HttpResponseMessage response = await client.PostAsync(url,
request).ConfigureAwait(false))
using (HttpContent content = response.Content)
{
string data = await content.ReadAsStringAsync().ConfigureAwait(false);
return JsonObject.Parse(data);
}
}
}
Any help appreciated!
change
await content.ReadAsStringAsync().ConfigureAwait(false)
to
content.ReadAsStringAsync().Result
the ReadAsStringAsync returns a Task object. the '.Result' in the end of the line tell the compiler to return the inner string.
That should do it:
static async Task<JObect> Upload(string key, string url, string
sourceFile, string targetFormat)
{
using (HttpClientHandler handler = new HttpClientHandler {
Credentials = new NetworkCredential(key, "")
})
using (HttpClient client = new HttpClient(handler))
{
var request = new MultipartFormDataContent();
request.Add(new StringContent(targetFormat), "target_format");
request.Add(new StreamContent(File.OpenRead(sourceFile)),
"source_file",
new FileInfo(sourceFile).Name);
using (HttpResponseMessage response = await client.PostAsync(url,request))
using (HttpContent content = response.Content)
{
string data = await content.ReadAsStringAsync();
return JsonObject.Parse(data);
}
}
}