Microsoft Graph SDK (C#) Group Mailbox - c#

I have a connected console application that I'm writing to help automate an upcoming tenant migration.
What I'm looking to do is remove all associations of the custom domains from our current tenant so I can add them to a new tenant.
What I have so far:
ConsoleApp is registered with AAD and API permissions have been assigned and granted consent.
I've imported the following:
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client
Initialized a graphClient:
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
And successfully made .GetAsync calls to the Graph.
What I need help with:
I'm trying to update a Group's email to 'gItem.mailNickname#tenantID'. I've got an error saying that the "Mail field is read-only". Thru the Graph Explorer, I noticed that the proxyAddresses field includes all the aliases for the group and that the Mail is listed as "SMPT:..." I'm trying to overwrite the proxyAddresses field with
var upGroup = await graphClient
.Groups["4f629be4-f592-4520-b80d-7570f68e276e"]
.Request()
.GetAsync();
var updateFields = new Group
{
ProxyAddresses = new List<string>()
{
"SMPT:" + upGroup.MailNickname + "#" + tenantID,
}
};
await graphClient
.Groups[upGroup.Id]
.Request()
.UpdateAsync(updateFields);
I get the error that there are insufficient permissions but have checked AAD to ensure that Directory.ReadWrite.All and Groups.ReadWrite.All are both provisioned and granted.
Other Group properties are able to be altered which leads me to question my structure on the proxyAddresses property.
Failed Code to add to the list
var updateFields = new Group
{
ProxyAddresses = new string[] { "SMTP:" + upGroup.MailNickname + "#" + tenantID }
};
Full Code:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
namespace RemoveGroupAliases
{
class Program
{
private static string clientId = "";
private static string tenantID = "";
private static string clientSecret = "";
private static IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
static ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
static async Task Main(string[] args)
{
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var groups = await graphClient
.Groups
.Request()
.Filter("mailEnabled+eq+true")
.GetAsync();
foreach (var tgroup in groups)
{
Console.WriteLine(tgroup.Id);
Console.WriteLine(tgroup.DisplayName);
};
var upGroup = await graphClient
.Groups["4f629be4-f592-4520-b80d-7570f68e276e"]
.Request()
.GetAsync();
var updateFields = new Group
{
ProxyAddresses = new List<String>()
{
"SMTP:" + upGroup.MailNickname + "#" + tenantID
}
};
await graphClient
.Groups[upGroup.Id]
.Request()
.UpdateAsync(updateFields);
Console.ReadLine();
}
}
}
The idea is to pull all mail-enabled groups and remove all domains so I can remove it from this tenant and associate it to a new tenant. I'll have a different program to assign the domains and aliases once the domains are switched over. When I hover over the tooltip for proxyAddresses one of the last lines says "Read-Only" which may be my issue but I'm not getting that as the error.

The scenario you're describing simply isn't possible. You cannot detach a Group (or really any data) from one tenant and migrate it to another. Every AAD tenant is a distinct entity and isolated from any other tenant.
Microsoft Graph uses the token you provide to route your requests to the correct API/Service within the tenant that generated the token. You cannot have a single token connected to multiple tenants.
Also, as noted in the documentation, the properties mail and proxyAddresses are read-only. If you wish to change the group's email alias, you can change the mailNickname property. This is simply the "alias" for the Group, the domain portion of the email address is automatically assigned by AAD and Exchange ({mailNickname}#{default.tenant.domain}).

Related

Problem to send message to teams channel with Microsoft graph

I'm working to send a message to Teams by using Graph API.
my application is a daemon application that sends a message automatically in the background.
I have written code like an official reference link below:
https://learn.microsoft.com/en-gb/graph/sdks/choose-authentication-providers?tabs=CS#client-credentials-provider
in my case, I use the client-credentials-provider but, I still can't send a message, and always get the below error message.
surely I have already registered my application in Azure and set for the grant of scope
How can I fix this?
Following this api document, you need to give Application api permission Teamwork.Migrate.All, and try this code below:
using Azure.Identity;
using Microsoft.Graph;
public void sendMesgAsync()
{
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";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var chatMessage = new ChatMessage
{
Body = new ItemBody
{
Content = "Hello World"
}
};
await graphClient.Teams["{team-id}"].Channels["{channel-id}"].Messages
.Request()
.AddAsync(chatMessage);
}

Get members of a Security Group from Azure AD via Microsoft Graph

I have been trying to get the members of a specific securitygroup from Azure AD with the following code from Graph api
var members = await graphClient.Groups["{group-id}"].Members
.Request()
.GetAsync();
I followed the following link which says to give the following permission to the registered app link: https://learn.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-1.0&tabs=csharp
and the permission for the application has been granted by the Admin;
But I keep getting the following error
ServiceException: Code: Authorization_RequestDenied Message: Insufficient privileges to complete the operation
I Using a client secret to create graphClient. And I grant permission like below, it works for me. You also can use other provider to do that.
My test code
public async Task<JsonResult> test()
{
// Values from app registration
var clientId = "fb2****-29ee-****-ab90-********0c7e1";
var clientSecret = "w7N*******........***yO8ig";
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "e4c9ab4e-****-40d5-****-230****57fb";
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
try
{
var members = await graphClient.Groups["13ad4665-****-43e9-9b0f-ca****eb"].Members.Request().GetAsync();
return Json(members);
}
catch (Exception e)
{
return Json("");
throw;
}
}
My test result
1st step : you will have to register an AD app and give permission on graph to read users and groups, please check this stackoverflow answer

Permissions problems reading users from azure AD with User.ReadBasic.All?

I am trying to get azure users and i am getting permissions error, even if instead of Users i place ME, why ? shouldnt it be something that i had no need to have admin consent? Any help is appreciated!!
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("****")
.WithTenantId("****")
.WithClientSecret("***")
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
// Create a new instance of GraphServiceClient with the authentication provider.
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = await graphClient.Users
.Request()
.WithScopes(graphScopes)
.Select(u => new {
u.DisplayName
})
.GetAsync();
```
For this problem it seems you do not have permission to get the users. You can refer to the document of the graph api, you need the permission shown as below:
So please go to the application which registered in your azure ad, and click "API permissions", then add the permission into it.
After add the permissions, please do not forget click "Grand admin consent for xxx".
For the question why it still failed when you change the Users to me. You use client credential flow to do authentication to request the graph api, you just provide the information of clientId, tenantId and clientSecret. So it doesn't contain a user(or yourself) information. So you can't use .Me. If you want to use .Me, you can use password grant flow. It contains the user information, so system know who is .Me.
================================Update==============================
If you want to use delegated permission(such as User.ReadBasic.All), you can't use client_credential. Now your code use client_credential flow, the access token doesn't contain user identity. I provide a sample of username/password flow(and use delegated permission User.ReadBasic.All) below for your referencef:
In the "API permissions" tab of the registered app, I just add one permission User.ReadBasic.All.
The code shown as below:
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Security;
namespace ConsoleApp3
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create("<clientId>")
.WithTenantId("<tenantId>")
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var str = "<your password>";
var password = new SecureString();
foreach (char c in str) password.AppendChar(c);
var users = await graphClient.Users.Request().WithUsernamePassword("<your account/email>", password).GetAsync();
Console.WriteLine(users.Count);
}
}
}
And before run the code, you need to do "consent to use the application" once. You need to browse the url as this: https://login.microsoftonline.com/<tenantId>/oauth2/v2.0/authorize?client_id=<clientId>&response_type=code&redirect_uri=<redirectUri>&response_mode=query&scope=openid https://graph.microsoft.com/.default&state=12345 in your browser and the page will show as:
Click "Accept", then you can run the code to get user list success.

Insufficient privileges to add Azure AD user

I created a console application to create an Azure AD user as follows (doc referred: https://learn.microsoft.com/en-us/graph/api/user-post-users?view=graph-rest-1.0&tabs=http):
static async Task Main(string[] args)
{
var credential = new ClientCredential("<clientt-id>", "<client-seceret>");
var authProvider = new HttpRequestMessageAuthenticationProvider(
credential,
"https://login.windows.net/<tenant-id>",
"https://graph.microsoft.com/");
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = new User
{
AccountEnabled = true,
DisplayName = "Test User",
MailNickname = "testuser",
UserPrincipalName = "testuser#M365xxxxxxx.onmicrosoft.com ",
PasswordProfile = "xxxxxxxxxxxx"
OnPremisesImmutableId = "id"
};
await graphClient.Users
.Request()
.AddAsync(user);
}
API permissions added to app are Group.ReadWrite.All and User.ReadWrite.All.
On running this code, I see the following error:
Code: Authorization_RequestDenied
Message: Insufficient privileges to complete the operation.
What am I missing?
For this problem, I summarize the points below which you need to check:
1. It seems your code use client_credentials as grant flow to do the job, so please check you have added the permissions of "Application" but not "Delegated". And don't forget grant admin consent.
2. If still show Authorization_RequestDenied message, please remove the permission Group.ReadWrite.All because this permission is unnecessary. And the Group permission may affect other permissions in my past tests.
3. It seems you develop the specific code in class HttpRequestMessageAuthenticationProvider, actually there is an off-the-shelf SDK avaiable for us to use. I provide my code below for your reference, the code works fine to create a user.
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Threading.Tasks;
namespace ConsoleApp23
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("<client_id>")
.WithTenantId("<tenant_id>")
.WithClientSecret("<client_secret>")
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = new User
{
AccountEnabled = true,
DisplayName = "huryAdd",
MailNickname = "huryAdd",
UserPrincipalName = "huryAdd#xxx.onmicrosoft.com",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "Password0123"
},
OnPremisesImmutableId = "testOnPre"
};
await graphClient.Users.Request().AddAsync(user);
Console.WriteLine("====success====");
}
}
}
And also provide the packages installed in my project.
Install-Package Microsoft.Identity.Client -Version 4.16.1
Install-Package Microsoft.Graph
Install-Package Microsoft.Graph.Auth -IncludePrerelease
4. By the way, there is a blank space in the end of your UserPrincipalName. Please remove it, otherwise it will show invalid principal name.
Hope it helps~
I had the same issue and managed to solve it.
I used the Directory.ReadWrite.All permission but still experienced the problem with setting the OnPremisesImmutableId attribute for our users.
After a bit of investigation, it turned out that i had to assign the roles "Group Administrator" and "User Administrator" to my application in Azure AD Portal (Azure AD > Roles and administrators > Click each group > Add Assignments). After both these roles had been applied to my application, the problem disappeard.

Server-side task to query Office 365 account for new emails

I need a server-side task on my .NET 4.6.1/MVC 5 app that will periodically check a specific O365 email address for new emails and retrieve them if found. This seems like a stupidly simple task, but I cannot find documentation anywhere for creating a server-side process to accomplish this. The only documentation Microsoft seems to have is for OAuth2 and passing through credentials when users sign in. I don't want that. I want to check one specific account, that's it. How would I accomplish this?
These are the pages I've found. There are others, but all are along these lines.
Intro to the Outlook API - I don't see a way to use a service account with the v2 endpoint.
Get Started with the Outlook REST APIs - This is specific to logging users in with OAuth2, unhelpful for my purposes.
Intro to the Outlook API - I don't see a way to use a service account with the v2 endpoint.
The v2 endpoint doesn’t support client credential at present( refer to the limitation). You need to register/configure the app using Azure portal and use the original endpoint to authenticate the app. More detail about register the app please refer to here. And we need to ‘read mail in all mailbox’ to use the client credential to read the messages like figure below.
And here is the code that using client credential to read messages using the Microsoft Graph:
string clientId = "";
string clientsecret = "";
string tenant = "";
string resourceURL = "https://graph.microsoft.com";
string authority = "https://login.microsoftonline.com/" + tenant + "/oauth2/token";
string userMail = "";
var accessToken = new TokenHelper(authority).AcquireTokenAsync(clientId, clientsecret, resourceURL);
var graphserviceClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.FromResult(0);
}));
var items = await graphserviceClient.Users[user].Messages.Request().OrderBy("receivedDateTime desc").GetAsync();
foreach (var item in items)
{
Console.WriteLine(item.Subject);
}
class TokenHelper
{
AuthenticationContext authContext;
public TokenHelper(string authUri)
{
authContext = new AuthenticationContext(authUri);
}
public string AcquireTokenAsync(string clientId, string secret,string resrouceURL)
{
var credential = new ClientCredential(clientId: clientId, clientSecret: secret);
var result = authContext.AcquireTokenAsync(resrouceURL, credential).Result;
return result.AccessToken;
}
}
In addition, if we authenticate the app with code grant flow we can also create a subscription which notify the app when the mail box receive the new messages.( refer to webhoocks/subscription)

Categories