how to create a web service for microsoft graph (C#) - c#

I've recently started working as a junior c# developer. My boss asked me to build methods to CRUD teams in our AAD using the microsoft graph API. I've achieved this with a test application like that:
public async Task<string> createTeam()
{
// readying data from registry
var clientId = "********************"; //application (client) ID
var clientSecret = "********************";
var redirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient";
var authority = "https://login.microsoftonline.com/********************/v2.0";
var cca = ConfidentialClientApplicationBuilder.Create(clientId)
.WithAuthority(authority)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.Build();
List<string> scopes = new List<string>
{
"https://graph.microsoft.com/.default"
};
//
var authenticationProvider = new MsalAuthenticationProvider(cca, scopes.ToArray());
//
GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);
// Code to create a Team
var team = new Team
{
DisplayName = "0000My Sample Team",
Description = "My Sample Team’s Description",
AdditionalData = new Dictionary<string, object>()
{
{"template#odata.bind", "https://graph.microsoft.com/v1.0/teamsTemplates('standard')"}
}
};
var result = await graphClient.Teams.Request().AddAsync(team);
return result.DisplayName;
}
With that piece of code working, I've created an asp.net web Application (.net framework) and added the class to it. The plan was to deploy it to an IIS server and and publish the methods as web services.
[WebMethod]
public async Task<string> createTeamAsync()
{
//The class where my methods reside
TeamServices ts = new TeamServices();
var result = await ts.createTeam();
return "OK";
}
I registered the app and deployed but when I try to use it, it does not create any Team.
Do you know what I'm doing wrong of what I should learn next get the app working? Im just a few weeks in c# and I'm completely lost in all that .net echosystem
Thanks in advance

Related

I need to retrieve the Application Insights's InstrumentationKey using C# code

I have created the application Insights using ARM template with C# code.
var creds = new AzureCredentialsFactory().FromServicePrincipal(client, key, tenant, AzureEnvironment.AzureGlobalCloud);
IAzure azure = Microsoft.Azure.Management.Fluent.Azure.Authenticate(creds).WithSubscription(subscription);
IDeployment deployement = azure.Deployments.Define("my-app")
.WithExistingResourceGroup("my-rg-grp")
.WithTemplate(template)
.WithParameters("{}")
.WithMode(DeploymentMode.Incremental)
.CreateAsync();
deployment doesn't have the InstrumentationKey in response.
How could I get the InstrumentationKey just after the Application Insights creation using ARM?
You can use ApplicationInsightsManagementClient class to get the ApplicationInsights resources and the relevant property. The class is defined at Microsoft.Azure.Management.ApplicationInsights v0.3.0-preview package
ApplicationInsightsManagementClient applicationInsightsManagementClient =
new ApplicationInsightsManagementClient(creds) { SubscriptionId = subscriptionId };
var appliationInsightComponents = await applicationInsightsManagementClient.Components.ListAsync();
var requiredApplicationInsightComponent = appliationInsightComponents.SingleOrDefault(a =>
a.ApplicationId.Equals("<<Name of resource>>", StringComparison.OrdinalIgnoreCase));
// to get the InstrumentationKey use
requiredApplicationInsightComponent.InstrumentationKey

Updating Azure Application via Microsoft Graph SDK

I am using the Microsoft Graph SDK (https://github.com/microsoftgraph/msgraph-sdk-dotnet) in my .NET Core 3.1 project logged in to my Service Principal. I can retrieve the Azure Application's Web Reply URLs:
var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var rootItem = await graphClient.Applications.Request().GetAsync();
Microsoft.Graph.Application app = new Microsoft.Graph.Application();
foreach (var item in rootItem)
{
if (item.DisplayName.Equals("MyApp"))
{
app = item;
}
}
I am able to retrieve the actual Application object just fine. My question is, how do I update app.Web.RedirectUris through the SDK? I am able to do so via the Azure CLI with az ad app update --id <my_app_id> --reply-urls <url_1> <url_2>
You need to get an Microsoft.Graph.IApplicationRequestBuilder for the specific Application and then call UpdateAsync() method.
var rootItem = await client.Applications.Request().GetAsync();
Microsoft.Graph.Application app = new Microsoft.Graph.Application();
foreach (var item in rootItem)
{
if (item.DisplayName.Equals("MyApp"))
{
app = item;
app.Web.RedirectUris = new List<string> { "uri1", "uri2" };
await client.Applications[app.Id].Request().UpdateAsync(app);
}
}

How do I use Service Principal authentication with an Azure Machine Learning Pipeline Endpoint in C#?

I'm trying to call an Azure Machine Learning Pipeline Endpoint I've set up using C# & the Machine Learning REST api.
I am certain that I have the Service Principal configured correctly, as I can successfully authenticate & hit the endpoint using the azureml-core python sdk:
sp = ServicePrincipalAuthentication(
tenant_id=tenant_id,
service_principal_id=service_principal_id,
service_principal_password=service_principal_password)
ws =Workspace.get(
name=workspace_name,
resource_group=resource_group,
subscription_id=subscription_id,
auth=sp)
endpoint = PipelineEndpoint.get(ws, name='MyEndpoint')
endpoint.submit('Test_Experiment')
I'm using the following example in C# to attempt to run my endpoint: https://learn.microsoft.com/en-us/azure/machine-learning/how-to-deploy-pipelines#run-a-published-pipeline-using-c
I'm attempting to fill auth_key with the following code:
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var cred = new ClientSecretCredential(tenantId, clientId, clientSecret);
var auth_key = cred.GetToken(new Azure.Core.TokenRequestContext(new string[] {".default" }));
I receive a 401 (unauthorized).
What am I am doing wrong?
UPDATE *
I changed the 'scopes' param in the TokenRequestContext to look like:
var auth_key = cred.GetToken(new Azure.Core.TokenRequestContext(new string[] { "http://DataTriggerApp/.default" }));
http://DataTriggerApp is one of the servicePrincipalNames that shows up when i query my Service Principal from the azure CLI.
Now, when I attempt to use the returned token to call the Machine Learning Pipeline Endpoint, I receive a 403 instead of a 401. Maybe some progress?
Ok, through a lot of trial-and-error I was able to come up with two ways of acquiring a token that allows me to hit my Azure Machine Learning Pipeline Endpoint through the REST api. One uses Microsoft.Identity.Client & one uses Azure.Identity.
using Microsoft.Identity.Client;
...
public static async Task<string> GetAccessToken()
{
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(AzureCloudInstance.AzurePublic, tenantId)
.Build();
var result = await app.AcquireTokenForClient(new string[] { "https://ml.azure.com/.default" }).ExecuteAsync();
return result.AccessToken;
}
Or:
using Azure.Identity;
...
public static async Task<string> GetAccessToken()
{
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var cred = new ClientSecretCredential(tenantId, clientId, clientSecret);
var token = await cred.GetTokenAsync(new Azure.Core.TokenRequestContext(new string[] { "https://ml.azure.com/.default" }));
return token.Token;
}

Azure DevOps API for create new projects and pipeline

In my organization there are a lot of researchers and each of them has few projects. For each project, I have to create a repository in Azure DevOps and a pipeline. I'm using .NET Core 3.1 but I can update to .NET5.
Apart from the name, the pipeline is the same for each project. What I want to create is a simple internal website where each researcher can add its project number and automatically the website call Azure DevOps via API to create a new repository and a new pipeline.
I tried to run the project from the Microsoft repository on GitHub but the examples don't work. What I want to achieve is a very simple piece of code that can create a new repository with an associate pipeline.
I saw the branch OAuthWebSampleAspNetCore.csproj in the same Microsoft repository. There is a OAuthController that seems correct based on the Microsoft documentation but it doesn't work. The following code creates the request to Azure DevOps.
private String BuildAuthorizationUrl(String state)
{
UriBuilder uriBuilder = new UriBuilder(this.Settings.AuthorizationUrl);
var queryParams = HttpUtility.ParseQueryString(uriBuilder.Query ?? String.Empty);
queryParams["client_id"] = this.Settings.ClientApp.Id.ToString();
queryParams["response_type"] = "Assertion";
queryParams["state"] = state;
queryParams["scope"] = this.Settings.ClientApp.Scope;
queryParams["redirect_uri"] = this.Settings.ClientApp.CallbackUrl;
uriBuilder.Query = queryParams.ToString();
return uriBuilder.ToString();
}
This is the URL the application calls
https://app.vssps.visualstudio.com:443/oauth2/authorize?client_id=myClientId&response_type=Assertion&state=6ca228c6-f73f-48be-9a0a-38c8f2483837&scope=myListOfScopes&redirect_uri=https%3a%2f%2flocalhost%3a43742%2foauth%2fcallback
and this is the result from Azure DevOps.
The next part is the callback. Based on the Microsoft documentation, I have to call the token URL https://app.vssps.visualstudio.com/oauth2/token as POST. The following code is how I do the request.
public async Task<ActionResult> Callback(String code, Guid state)
{
TokenViewModel tokenViewModel = new TokenViewModel() { OAuthSettings = this.Settings };
string error;
if (ValidateCallbackValues(code, state.ToString(), out error))
{
// Exchange the auth code for an access token and refresh token
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, this.Settings.TokenUrl);
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Dictionary<String, String> form = new Dictionary<String, String>()
{
{ "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" },
{ "client_assertion", GetClientAppSecret() },
{ "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" },
{ "assertion", code },
{ "redirect_uri", this.Settings.ClientApp.CallbackUrl }
};
requestMessage.Content = new FormUrlEncodedContent(form);
HttpResponseMessage responseMessage = await s_httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode)
{
String body = await responseMessage.Content.ReadAsStringAsync();
Token token = s_authorizationRequests[state];
JsonConvert.PopulateObject(body, token);
tokenViewModel.Token = token;
}
else
{
error = responseMessage.ReasonPhrase;
}
}
else
{
tokenViewModel.Error = error;
}
return View("TokenView", tokenViewModel);
}
Although, I think, I have a valid token and IsSuccessStatusCode is always false because a BadRequest.
Are there any updated samples I can use? Did someone face the same issues?
https://app.vssps.visualstudio.com:443/oauth2/authorize?client_id=myClientId&response_type=Assertion&state=6ca228c6-f73f-48be-9a0a-38c8f2483837&scope=myListOfScopes&redirect_uri=https%3a%2f%2flocalhost%3a43742%2foauth%2fcallback
I found you used your localhost as your Callback URL.
You cannot use your localhost as the callback url, for your localhost is unavailable to azure devops services.
You have to use a callback url that can be accessed from the public network. Or you will get above 400-bad request error.

404 error while creating Online Meeting using microsoft graph api c# without login into AzureActiveDirectory

I am trying to create Online Meeting using microsoft graph api without login into AzureActiveDirectory with asp.net web application.For this my app has below permissions which are required as per documentation https://learn.microsoft.com/en-us/graph/api/application-post-onlinemeetings?view=graph-rest-1.0&tabs=csharp with client credential auth flow https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow without immediate interaction with a user.I am able to retrive access token successfully as per client-creds-grant-flow.
I tried Micosoft.Graph and Micosoft.Graph.Beta still getting 404 error.
Create online meeting code
var graphClient = GetAuthenticatedClientCredential();
var onlineMeeting = new OnlineMeeting
{
StartDateTime = DateTimeOffset.Parse("2020-10-01T10:30:34.2444915+00:00"),
EndDateTime = DateTimeOffset.Parse("2020-10-01T11:00:34.2464912+00:00"),
Subject = "Create Online Meeting-Without user login to Office 365"
};
return await graphClient.Me.OnlineMeetings
.Request()
.AddAsync(onlineMeeting);
Access Token code
public static async Task<string> GetUserAccessTokenAsyncByCc()
{
IConfidentialClientApplication cca = ConfidentialClientApplicationBuilder.Create(appId)
.WithTenantId(appTenantId)
.WithClientSecret(appSecret)
.Build();
string[] scopes1 = new string[] { "https://graph.microsoft.com/.default" };
//string[] scopes1 = new string[] { "https://graph.microsoft.com/OnlineMeetings.ReadWrite.All" };
// string[] scopes1 = new string[] { "https://graph.microsoft.com/beta/OnlineMeetings.Read.All" };
//string[] scopes1 = new string[] { "https://graph.microsoft.com/beta/.default" };
var result = await cca.AcquireTokenForClient(scopes1).ExecuteAsync();
return result.AccessToken;
}
and Auth Provider code
public static GraphServiceClient GetAuthenticatedClientCredential()
{
DelegateAuthenticationProvider provider = new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string accessToken = await GetUserAccessTokenAsyncByCc();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
});
GraphServiceClient graphClient = new GraphServiceClient(provider);
return graphClient;
}
app permission image
below are the necessary app permission
You can only use delegated permissions to create an onlineMeeting, so you must log in as a user, and you cannot use the client credential flow. You need to use the auth code flow to obtain the token.

Categories