Last active
June 30, 2026 13:30
-
-
Save machv/506c4ffd2b693505ee229a291a39bd36 to your computer and use it in GitHub Desktop.
App only to mailbox
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <Project Sdk="Microsoft.NET.Sdk"> | |
| <PropertyGroup> | |
| <OutputType>Exe</OutputType> | |
| <TargetFramework>net9.0</TargetFramework> | |
| <ImplicitUsings>enable</ImplicitUsings> | |
| <Nullable>enable</Nullable> | |
| </PropertyGroup> | |
| <ItemGroup> | |
| <None Update="appsettings.json"> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| </None> | |
| </ItemGroup> | |
| <ItemGroup> | |
| <PackageReference Include="Azure.Identity" Version="1.21.0" /> | |
| <PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.9" /> | |
| <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.9" /> | |
| <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.9" /> | |
| <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.9" /> | |
| <PackageReference Include="Microsoft.Graph" Version="6.2.0" /> | |
| </ItemGroup> | |
| </Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| namespace AppRegExo; | |
| /// <summary> | |
| /// Strongly-typed configuration bound from the "ExchangeOnline" section of appsettings.json. | |
| /// </summary> | |
| public sealed class AppSettings | |
| { | |
| /// <summary>Azure AD (Entra ID) directory/tenant ID.</summary> | |
| public string TenantId { get; set; } = string.Empty; | |
| /// <summary>Application (client) ID of the app registration.</summary> | |
| public string ClientId { get; set; } = string.Empty; | |
| /// <summary>Client secret value of the app registration.</summary> | |
| public string ClientSecret { get; set; } = string.Empty; | |
| /// <summary>Email address (UPN) of the shared mailbox to read.</summary> | |
| public string Mailbox { get; set; } = string.Empty; | |
| /// <summary> | |
| /// When true, enables verbose Azure SDK / authentication logging to help troubleshoot | |
| /// sign-in and token acquisition errors. Optional; defaults to false. | |
| /// </summary> | |
| public bool Verbose { get; set; } | |
| /// <summary> | |
| /// Returns a human-readable error if any required value is missing, otherwise null. | |
| /// </summary> | |
| public string? Validate() | |
| { | |
| if (string.IsNullOrWhiteSpace(TenantId)) return "TenantId is missing."; | |
| if (string.IsNullOrWhiteSpace(ClientId)) return "ClientId is missing."; | |
| if (string.IsNullOrWhiteSpace(ClientSecret)) return "ClientSecret is missing."; | |
| if (string.IsNullOrWhiteSpace(Mailbox)) return "Mailbox is missing."; | |
| return null; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "ExchangeOnline": { | |
| "TenantId": "00000000-0000-0000-0000-000000000000", | |
| "ClientId": "00000000-0000-0000-0000-000000000000", | |
| "ClientSecret": "your-client-secret-value", | |
| "Mailbox": "shared-mailbox@contoso.com", | |
| "Verbose": false | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Diagnostics.Tracing; | |
| using Azure.Core.Diagnostics; | |
| using Azure.Identity; | |
| using Microsoft.Extensions.Configuration; | |
| using Microsoft.Graph; | |
| namespace AppRegExo; | |
| internal static class Program | |
| { | |
| private const string GraphDefaultScope = "https://graph.microsoft.com/.default"; | |
| private const int MessageCount = 10; | |
| private static async Task<int> Main() | |
| { | |
| // Load configuration from appsettings.json (optional, copied next to the executable) | |
| // and environment variables (e.g. ExchangeOnline__ClientSecret=...), which is handy for | |
| // running in containers without baking secrets into the image. | |
| IConfiguration configuration = new ConfigurationBuilder() | |
| .SetBasePath(AppContext.BaseDirectory) | |
| .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) | |
| .AddEnvironmentVariables() | |
| .Build(); | |
| var settings = configuration.GetSection("ExchangeOnline").Get<AppSettings>() ?? new AppSettings(); | |
| var validationError = settings.Validate(); | |
| if (validationError is not null) | |
| { | |
| Console.Error.WriteLine($"Configuration error: {validationError}"); | |
| Console.Error.WriteLine("Fill in appsettings.json (see appsettings.sample.json for the expected shape)."); | |
| return 1; | |
| } | |
| // Optional verbose logging to help troubleshoot sign-in / token acquisition errors. | |
| // Enable via "ExchangeOnline:Verbose": true in appsettings.json, or the environment | |
| // variable ExchangeOnline__Verbose=true. The listener captures Azure Identity (and the | |
| // underlying MSAL) diagnostics and writes them to the console for the lifetime of the run. | |
| using AzureEventSourceListener? azureLogListener = settings.Verbose | |
| ? AzureEventSourceListener.CreateConsoleLogger(EventLevel.Verbose) | |
| : null; | |
| if (settings.Verbose) | |
| { | |
| Console.WriteLine("Verbose logging enabled — Azure Identity and Graph diagnostics will be written to the console."); | |
| Console.WriteLine(); | |
| } | |
| try | |
| { | |
| // App-only authentication using the client credentials flow. | |
| var credentialOptions = new ClientSecretCredentialOptions(); | |
| if (settings.Verbose) | |
| { | |
| // Surface detailed authentication diagnostics, including account identifiers (PII) | |
| // that are normally redacted, which makes login failures much easier to diagnose. | |
| credentialOptions.Diagnostics.IsLoggingEnabled = true; | |
| credentialOptions.Diagnostics.IsAccountIdentifierLoggingEnabled = true; | |
| } | |
| var credential = new ClientSecretCredential( | |
| settings.TenantId, settings.ClientId, settings.ClientSecret, credentialOptions); | |
| var graphClient = new GraphServiceClient(credential, new[] { GraphDefaultScope }); | |
| Console.WriteLine($"Reading the first {MessageCount} messages from the Inbox of '{settings.Mailbox}'..."); | |
| Console.WriteLine(); | |
| var response = await graphClient | |
| .Users[settings.Mailbox] | |
| .MailFolders["Inbox"] | |
| .Messages | |
| .GetAsync(request => | |
| { | |
| request.QueryParameters.Top = MessageCount; | |
| request.QueryParameters.Orderby = new[] { "receivedDateTime desc" }; | |
| request.QueryParameters.Select = new[] { "subject", "from", "receivedDateTime" }; | |
| }); | |
| var messages = response?.Value; | |
| if (messages is null || messages.Count == 0) | |
| { | |
| Console.WriteLine("No messages found."); | |
| return 0; | |
| } | |
| var index = 1; | |
| foreach (var message in messages) | |
| { | |
| var received = message.ReceivedDateTime?.ToLocalTime().ToString("yyyy-MM-dd HH:mm") ?? "(unknown date)"; | |
| var sender = message.From?.EmailAddress?.Address ?? "(unknown sender)"; | |
| var subject = string.IsNullOrEmpty(message.Subject) ? "(no subject)" : message.Subject; | |
| Console.WriteLine($"{index,2}. [{received}] {sender}"); | |
| Console.WriteLine($" {subject}"); | |
| index++; | |
| } | |
| return 0; | |
| } | |
| catch (Exception ex) | |
| { | |
| Console.Error.WriteLine("Failed to retrieve messages from Exchange Online."); | |
| Console.Error.WriteLine(settings.Verbose ? ex.ToString() : ex.Message); | |
| if (!settings.Verbose) | |
| { | |
| Console.Error.WriteLine("Set \"ExchangeOnline:Verbose\" to true (or ExchangeOnline__Verbose=true) for detailed authentication diagnostics."); | |
| } | |
| return 1; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment