Azure SqlManagementClient - Forbidden with TokenCloudCredentials - c#

I am trying to connect to my SQL server in azure and list the dbs from a .net application, but I keep getting
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
even though i am trying to use the Sql Management client with TokenCloudCredentials.
var authContext = new AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientId, appKey);
var result = authContext.AcquireTokenAsync(resource, clientCredential).Result;
var credentials = new Microsoft.Azure.TokenCloudCredentials(subscriptionId, result.AccessToken);
var client = new SqlManagementClient(credentials);
try
{
var servers = await client.Servers.ListAsync();
}
catch (CloudException c)
{
Console.WriteLine(c.Message);
throw;
}
The AD application have permissions to access the resource group and the Azure Management API.
Any ideas why it keeps complaining about a certificate, while using token?
EDIT: I managed to do it using the "new" fluent management API. You need to create an AD application associated with the subscription and have access to the resource group. Then just create credentials and initialize the fluent API.
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AzureManagement
{
public class Program
{
public static void Main(string[] args)
{
var azureCredentials = new AzureCredentials(new
ServicePrincipalLoginInformation
{
ClientId = "clientId",
ClientSecret = "clientSecret="
}, "tenantId", AzureEnvironment.AzureGlobalCloud);
var _azure = Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(azureCredentials)
.WithSubscription("subscriptionId");
var sql = _azure.SqlServers.List().ToList();
foreach (var s in sql)
{
var dbs = s.Databases.List().ToList();
}
Console.ReadLine();
}
}
}

I am not sure if this is supported. Please create a support case for this problem.

The code works like this.
You have to assign a role - in my use case I assigned the contributor role to the App registration, on to the Ressource Group

Related

Get users contacts by Graph SDK

I try to read all users contacts by using graph sdk and c# but in the user at the response always the array of contacts is null even though the user has contacts
I was requesting all user's contacts from exchange online with graph sdk and c#, but
var graphResult = graphClient.Users.Request().GetAsync().Result;
Console.WriteLine(graphResult[0].Contacts[0]);
returns NullReferenceException.
I granted following privileges:
the following token is set in azure
here you can see my tenant id and so on
Main Class
using Microsoft.Graph;
using Azure.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using System.Data.SqlClient;
namespace ExchangeTestAppKonsole
{
internal class Program
{
static void Main(string[] args)
{
getContacts();
Console.ReadLine();
}
public static void getContacts()
{
var clientId = "de196208-b4d7-468f-8fa4-7328551566b9";
var clientSecret = "~uG8Q~~vrTGuaIPfzeIR9GUUpSK5aaG.KZTYGcnD";
var redirectUri = "https://global.consent.azure-apim.net/redirect";
var authority = "https://login.microsoftonline.com/0be300e6-91fd-4638-bcd1-40d742ef6ece/v2.0";
var cca = ConfidentialClientApplicationBuilder.Create(clientId)
.WithAuthority(authority)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.Build();
// use the default permissions assigned from within the Azure AD app registration portal
List<string> scopes = new List<string>();
scopes.Add("https://graph.microsoft.com/.default");
var authenticationProvider = new MsalAuthenticationProvider(cca, scopes.ToArray());
GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);
var graphResult = graphClient.Users.Request().GetAsync().Result;
Console.WriteLine(graphResult[0].Contacts[0]);
}
}
}
Authentication Provider
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace ExchangeTestAppKonsole
{
internal class MsalAuthenticationProvider : IAuthenticationProvider
{
private IConfidentialClientApplication _clientApplication;
private string[] _scopes;
public MsalAuthenticationProvider(IConfidentialClientApplication clientApplication, string[] scopes)
{
_clientApplication = clientApplication;
_scopes = scopes;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
public async Task<string> GetTokenAsync()
{
AuthenticationResult authResult = null;
authResult = await _clientApplication.AcquireTokenForClient(_scopes).ExecuteAsync();
return authResult.AccessToken;
}
}
}
I also requested the contacts of the first user by logging in with this user into graphExplorer
and requested the /me/contacts endpoint it shows 3 contacts
it seems to be a premissions thing but i've no idea what exactly the problem is.
Contacts is a relationship on User resource type.
If you want to include some relationship in the response, you need to specify the relationship in Expand.
Not all relationships and resources support the expand. According to the documentation there is no mention that contacts supports expand, so probably this code won't work.
var graphResult = graphClient.Users
.Request()
.Expand("contacts")
.GetAsync().Result();
Console.WriteLine(graphResult[0].Contacts[0]);
In that case you need to make a separate call for each user to get contacts.
var contacts = await graphClient.Users["{user_id}"].Contacts
.Request()
.GetAsync();
Users relationships

Microsoft Graph API get multiple B2C user via email address

I have the use case to query multiple b2c user from the microsoft graph api to get the display name and get the information about the last login. I am aware that the last login is just available via the beta route.
I am using the microsoft graph api beta client and try to get a user via an email address.
My b2c users do not have any mail or otherMail values, only information about the email is placed in the identities list.
var result = await client.Users
.Request()
.Select(e => new
{
e.DisplayName,
e.UserType,
e.OtherMails,
e.UserPrincipalName,
e.Mail,
e.Identities,
e.SignInActivity
}).GetAsync();
This Call returns all user, so I would have to filter in memory which would be bad.
.Filter("identities/any(id:id/issuer eq 'xxx.onmicrosoft.com' and id/issuerAssignedId eq 'superUser#mail.com')")
This filter function returns exactly one specific user, but I wasn't able to query multiple users via a single request. Something like
.Filter("identities/any(id:id/issuer eq 'xxx.onmicrosoft.com' and id/issuerAssignedId eq 'superUser#mail.com') or identities/any(id:id/issuer eq 'xxx.onmicrosoft.com' and id/issuerAssignedId eq 'superUser2#mail.com')")
Return query is to complex an replace the 'eq' with an 'in' returns not supported query, because looks like lambda operators do not support 'in'.
Has someone an idea how to query for e.g. 2 emails addresses with an single request?
I also can provide an rosly pad script were you just have to set your specific values like client id, secret and so on.
#r "nuget:Microsoft.Graph.Auth/1.0.0-preview.7"
#r "nuget:Microsoft.Graph.Beta/4.28.0-preview"
#r "nuget:RestSharp/107.1.1"
#r "nuget:RestRequest/1.2.0"
#r "nuget:Microsoft.Azure.Services.AppAuthentication/1.6.2"
#r "nuget:Azure.Core/1.22.0"
#r "nuget:Azure.Identity/1.5.0"
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net.Http.Headers;
using Azure.Identity;
using System.Linq;
using Azure.Core;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System.Net.Http;
var client = await GetGraphApiClient();
var emails = new []{ "email1#example.de","email2#example.de","email3#example.de","email4#example.de","email5#example.de"};
// Build the batch
var batchRequestContent = new BatchRequestContent();
// Using AddBatchRequestStep adds each request as a step
foreach (var element in emails)
{
var userRequest2 = client.Users
.Request()
.Select(e => new
{
e.DisplayName,
e.UserType,
e.OtherMails,
e.UserPrincipalName,
e.Mail,
e.Identities,
e.SignInActivity // just provided in the Microsoft.Graph.Beta package
})
.Filter($"identities/any(id:id/issuer eq ' ' and id/issuerAssignedId eq '{element}')");
batchRequestContent.AddBatchRequestStep(userRequest2);
}
var returnedResponse = await client.Batch.Request().PostAsync(batchRequestContent);
try
{
var user = await returnedResponse
.GetResponsesAsync();
user.Dump();
}
catch (ServiceException ex)
{
Console.WriteLine($"Failed to get user: {ex.Error.Message}");
}
private static async Task<GraphServiceClient> GetGraphApiClient()
{
var clientId = "<Client-Id-Of-your-app-with-graph-access>";
var secret = "<Client-Secret-Of-your-app-with-graph-access>";
var tenant = "<tenant-id>";
string[] scopes = new string[] { "AuditLog.Read.All", "User.Read.All" };
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenant)
.WithClientSecret(secret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
var serviceToken = await authProvider.ClientApplication.AcquireTokenForClient(new string[] { "https://graph.microsoft.com/.default" }).ExecuteAsync();
return new GraphServiceClient(authProvider);
}
Thanks Danstan,
with the a batch request it works to get with single request up to 20 accounts at once. https://learn.microsoft.com/en-us/graph/sdks/batch-requests?tabs=csharp#simple-batching-example
The API still limited it to 20 request in a single batch request. 'Code: MaximumValueExceeded
Message: Number of batch request steps exceeds the maximum value of 20.'
This makes it possible to query all the data by view requests.

Bring up Sharepoint prompt in .NET5

We used to be able to log into SharePoint Online using the SharePointOnlineCredentials class eg here.
This class is not available in .NET5 and I don't have the necessary Azure permissions to configure the application in Azure AD as described here
I'd like a C# program bring up a prompt, so the user can log into Sharepoint manually. Once the user has done this the program will do what it needs to do. Is there some C# code that will bring up the Sharepoint online login prompt?
I've downloaded Pnp and CSOM. This code contains the following errors.
The name 'GetSecureString' does not exist in the current context
No overload for method 'GetContext' takes 3 arguments
'Program.GetContext(Uri, string, SecureString)': not all code paths return a value
The name 'context' does not exist in the current context
using System;
using System.Security;
using Microsoft.SharePoint.Client;
using System.Threading.Tasks;
using PnP.Framework;
namespace ConsoleApplication1
{
class Program
{
public static async Task Main(string[] args)
{
Uri site = new Uri("https://contoso.sharepoint.com/sites/siteA");
string user = "joe.doe#contoso.onmicrosoft.com";
SecureString password = GetSecureString($"Password for {user}");
// Note: The PnP Sites Core AuthenticationManager class also supports this
using (var authenticationManager = new AuthenticationManager())
using (var context = authenticationManager.GetContext(site, user, password))
{
context.Load(context.Web, p => p.Title);
await context.ExecuteQueryAsync();
Console.WriteLine($"Title: {context.Web.Title}");
}
}
public ClientContext GetContext(Uri web, string userPrincipalName,
SecureString userPassword)
{
context.ExecutingWebRequest += (sender, e) =>
{
// Get an access token using your preferred approach
string accessToken = MyCodeToGetAnAccessToken(new Uri($"
{web.Scheme}://{web.DnsSafeHost}"), userPrincipalName, new
System.Net.NetworkCredential(string.Empty, userPassword).Password);
// Insert the access token in the request
e.WebRequestExecutor.RequestHeaders["Authorization"] =
"Bearer " + accessToken;
};
}
}
}

Microsoft Graph API query involving externalUserState C#

Hi I'm trying to write a simple console app which I intend to make into a batch file and get a list of external users who were invited by email have and now they have guest accounts in our Azure tenant and they have redeemed the url that was sent to them in email. When they redeem, their extenalUserState sets to "Accepted". I want to find which ones have that status.
I was told that I have to point to beta version of the API and not v.1.0 of the graph endpoint.
I have the following rudimentary code I have written looking at various examples I could find on GitHub/MS documentation for API etc.
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace CreateAzureADUser
{
class Program
{
static string TenantDomain;
static string TenantId;
static string ClientId;
static string ClientSecret;
static void Main(string[] args)
{
GetUsers();
//Console.WriteLine("------------------------------------------\n\n");
//GetGroupsAndMembers();
//CreateAzureADUserNow();
}
private static void GetUsers()
{
var graphServiceClient = CreateGraphServiceClient();
var users = graphServiceClient.Users.Request().Filter("userType eq 'Guest' and startswith(mail,'phs')")
.Select("id,mail,OnPremisesExtensionAttributes,userType,displayName,externalUserState")
.GetAsync()
.Result;
Console.WriteLine("Users found: {0}", users.Count);
Console.WriteLine();
foreach (var item in users)
{
Console.WriteLine("displayName: {3} \nuser id: {0} \nuser email: {1} \nExtensionAttribute8: {2}\n", item.Id, item.Mail, item.OnPremisesExtensionAttributes.ExtensionAttribute8, item.DisplayName);
}
}
public static GraphServiceClient CreateGraphServiceClient()
{
TenantDomain = "mycompanytenant.onmicrosoft.com";
TenantId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
ClientId = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
ClientSecret = "zzzzzzzzzzzz";
var clientCredential = new ClientCredential(ClientId, ClientSecret);
var authenticationContext = new AuthenticationContext($"https://login.microsoftonline.com/mycompanytenant.onmicrosoft.com");
var authenticationResult = authenticationContext.AcquireTokenAsync("https://graph.microsoft.com", clientCredential).Result;
var delegateAuthProvider = new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
});
// Use this for v.1.0 endpoint
//return new GraphServiceClient(delegateAuthProvider);
// Use this for connecting to beta endpoint
return new GraphServiceClient("https://graph.microsoft.com/beta", delegateAuthProvider);
}
}
}
When I run through the debugger, I do not see "ExternalUserState" as an attribute on the users that are returned.
How to access ExternalUserState attribute on the guest user object?
You're using the SDK so you're using Graph v1.0, not the Beta. The SDKs are all generated from v1.0 metadata so beta properties and methods simply do not exist in the models.
From time to time there is a beta build pushed out to GitHub but it is generally a few versions behind. Currently, the latest beta SDK available seems to be v1.12.0 (for reference, the current SDK is v1.15).

Get all members/users from TFS via REST API

I tried to get all members/users of TFS with the REST API and the .NET client libraries.
It works, but I get a maximum number of 50 identitys. Does anyone know, how I get all users, not only 50? (I prefer avoiding to use the old API, how it is suggested in this question)
Here is my code:
VssCredentials credentials = new VssCredentials();
VssConnection connection = new VssConnection(new Uri(url), credentials);
IdentityMruHttpClient identityMruHttpClient = connection.GetClient<IdentityMruHttpClient>();
List<IdentityRef> members = identityMruHttpClient.GetIdentityMruAsync(ProjectName).Result;
There is a REST API User Entitlements - List which can retrieve the user list from VSTS (Visual Studio Team Services), but it's only available for VSTS.
There isn't such a REST API to retrieve the user list from on-premise TFS (TFS 2017 in your scenario).
So, for now you can use the client API you mentioned above to retrieve the user list. Tested on my side, I can retrieve all the identities (more than 50 ) with below code:
You can also check the user list from userlist.txt file which under ..\..\ \bin\Debug\
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
using System.Linq;
using System.IO;
namespace Getuserlist
{
class Program
{
static void Main(string[] args)
{
TfsConfigurationServer tcs = new TfsConfigurationServer(new Uri("http://server:8080/tfs"));
IIdentityManagementService ims = tcs.GetService<IIdentityManagementService>();
TeamFoundationIdentity tfi = ims.ReadIdentity(IdentitySearchFactor.AccountName, "Project Collection Valid Users", MembershipQuery.Expanded, ReadIdentityOptions.None);
TeamFoundationIdentity[] ids = ims.ReadIdentities(tfi.Members, MembershipQuery.None, ReadIdentityOptions.None);
using (StreamWriter file = new StreamWriter("userlist.txt"))
foreach (TeamFoundationIdentity id in ids)
{
if (id.Descriptor.IdentityType == "System.Security.Principal.WindowsIdentity")
{ Console.WriteLine(id.DisplayName); }
//{ Console.WriteLine(id.UniqueName); }
file.WriteLine("[{0}]", id.DisplayName);
}
var count = ids.Count(x => ids.Contains(x));
Console.WriteLine(count);
Console.ReadLine();
}
}
}

Categories

Resources