Skip to content
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]

### Added
- Re-enabled `a365 create-instance` command (previously deprecated) — creates agent identity, agent user, assigns licenses, and grants admin consent for Graph/MCP/Bot Framework scopes in a single command
Comment thread
gwharris7 marked this conversation as resolved.
Outdated
- `Agent365.Observability.OtelWrite` scope now granted to all provisioned agent identities on the Observability API alongside `user_impersonation`, enabling agents to write OpenTelemetry data to the Agent 365 observability service
- `ChannelMessage.Read.All` and `ChannelMessage.Send` added to default blueprint Microsoft Graph delegated scopes (`agentIdentityScopes`)
- `Files.ReadWrite.All`, `ChannelMessage.Read.All`, and `ChannelMessage.Send` added to default blueprint Microsoft Graph application scopes (`agentApplicationScopes`)
Expand All @@ -18,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- `a365 publish` updates manifest IDs, creates `manifest.zip`, and prints concise upload instructions for Microsoft 365 Admin Center (Agents > All agents > Upload custom agent). Interactive prompts only occur in interactive terminals; redirect stdin to suppress them in scripts.

### Fixed
- `A365CreateInstanceRunner` sponsor handling: sponsor is now required (Graph API rejects requests without one) — removed fallback that silently stripped the sponsor on retry, which caused `BadRequest` errors
- Intermittent `ConnectionResetError (10054)` failures on corporate networks with TLS inspection proxies (Zscaler, Netskope) — Graph and ARM API calls now use direct MSAL.NET token acquisition instead of `az account get-access-token` subprocesses, bypassing the Python HTTP stack that triggered proxy resets (#321)
- `a365 cleanup` blueprint deletion now succeeds for Global Administrators even when the blueprint was created by a different user
- `a365 setup all` no longer times out for non-admin users — the CLI immediately surfaces a consent URL to share with an administrator instead of waiting for a browser prompt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ public class CreateInstanceCommand
public static Command CreateCommand(ILogger<CreateInstanceCommand> logger, IConfigService configService, CommandExecutor executor,
IBotConfigurator botConfigurator, GraphApiService graphApiService)
Comment thread
gwharris7 marked this conversation as resolved.
Outdated
{
// Command description - deprecated
// Old: Create and configure agent user identities with appropriate
// Command description
// Create and configure agent user identities with appropriate
// licenses and notification settings for your deployed agent
var command = new Command("create-instance", "DEPRECATED: Use 'a365 publish', optionally 'a365 deploy', then create instances in Microsoft Teams");
var command = new Command("create-instance", "Create and configure agent user identities with appropriate licenses and notification settings for your deployed agent");
Comment thread
gwharris7 marked this conversation as resolved.

// Options for the main create-instance command
var configOption = new Option<FileInfo>(
Expand All @@ -44,18 +44,13 @@ public static Command CreateCommand(ILogger<CreateInstanceCommand> logger, IConf
command.AddOption(verboseOption);
command.AddOption(dryRunOption);

// Subcommands removed - command is deprecated
// Keeping subcommand implementation code for local development only
// Subcommands
command.AddCommand(CreateIdentitySubcommand(logger, configService, executor, graphApiService));
command.AddCommand(CreateLicensesSubcommand(logger, configService, executor, graphApiService));

// Default handler
command.SetHandler(async (config, verbose, dryRun) =>
{
LogDeprecationError(logger, "create-instance");
Environment.Exit(1);

// Unreachable code below - preserved for local development use cases
#pragma warning disable CS0162 // Unreachable code detected

if (dryRun)
{
logger.LogInformation("DRY RUN: Agent 365 Instance Creation - All Steps");
Comment thread
gwharris7 marked this conversation as resolved.
Expand All @@ -82,19 +77,13 @@ public static Command CreateCommand(ILogger<CreateInstanceCommand> logger, IConf
logger.LogInformation("Step 1-3: Creating Agent Identity, adding licenses, and registering MCP servers...");
logger.LogInformation("");

// Use C# runner with AuthenticationService and GraphApiService
var cleanLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();
var authService = new AuthenticationService(
cleanLoggerFactory.CreateLogger<AuthenticationService>());

var graphService = new GraphApiService(
cleanLoggerFactory.CreateLogger<GraphApiService>(),
executor);
// Use C# runner with the DI-injected GraphApiService (has MSAL auth context)
using var cleanLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();

var instanceRunner = new A365CreateInstanceRunner(
cleanLoggerFactory.CreateLogger<A365CreateInstanceRunner>(),
executor,
graphService);
graphApiService);

var generatedConfigPath = Path.Combine(
config.DirectoryName ?? Environment.CurrentDirectory,
Expand Down Expand Up @@ -254,7 +243,7 @@ public static Command CreateCommand(ILogger<CreateInstanceCommand> logger, IConf
generatedConfigPath = Path.Combine(
config.DirectoryName ?? Environment.CurrentDirectory,
"a365.generated.config.json");
var syncLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();
using var syncLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();
var platformDetector = new PlatformDetector(syncLoggerFactory.CreateLogger<PlatformDetector>());

await ProjectSettingsSyncHelper.ExecuteAsync(
Expand Down Expand Up @@ -288,7 +277,8 @@ await ProjectSettingsSyncHelper.ExecuteAsync(
private static Command CreateIdentitySubcommand(
ILogger<CreateInstanceCommand> logger,
IConfigService configService,
CommandExecutor executor)
CommandExecutor executor,
GraphApiService graphApiService)
{
var command = new Command("identity", "Create Agent Identity and Agent User");

Expand All @@ -311,12 +301,6 @@ private static Command CreateIdentitySubcommand(

command.SetHandler(async (config, verbose, dryRun) =>
{
LogDeprecationError(logger, "create-instance identity");
Environment.Exit(1);

// Unreachable code below - preserved for local development
#pragma warning disable CS0162

if (dryRun)
{
logger.LogInformation("DRY RUN: Creating Agent Identity and Agent User");
Comment thread
gwharris7 marked this conversation as resolved.
Expand All @@ -333,19 +317,13 @@ private static Command CreateIdentitySubcommand(
var instanceConfig = await LoadConfigAsync(logger, configService, config.FullName);
if (instanceConfig == null) Environment.Exit(1);

// Use C# runner with AuthenticationService and GraphApiService
var cleanLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();
var authService = new AuthenticationService(
cleanLoggerFactory.CreateLogger<AuthenticationService>());

var graphService = new GraphApiService(
cleanLoggerFactory.CreateLogger<GraphApiService>(),
executor);
// Use C# runner with the DI-injected GraphApiService (has MSAL auth context)
using var cleanLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();

var instanceRunner = new A365CreateInstanceRunner(
cleanLoggerFactory.CreateLogger<A365CreateInstanceRunner>(),
executor,
graphService);
graphApiService);

var generatedConfigPath = Path.Combine(
config.DirectoryName ?? Environment.CurrentDirectory,
Expand All @@ -368,7 +346,7 @@ private static Command CreateIdentitySubcommand(
config.DirectoryName ?? Environment.CurrentDirectory,
"a365.generated.config.json");

var syncLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();
using var syncLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();
var platformDetector = new PlatformDetector(syncLoggerFactory.CreateLogger<PlatformDetector>());

await ProjectSettingsSyncHelper.ExecuteAsync(
Expand Down Expand Up @@ -402,7 +380,8 @@ await ProjectSettingsSyncHelper.ExecuteAsync(
private static Command CreateLicensesSubcommand(
ILogger<CreateInstanceCommand> logger,
IConfigService configService,
CommandExecutor executor)
CommandExecutor executor,
GraphApiService graphApiService)
{
var command = new Command("licenses", "Add licenses to Agent User");

Expand All @@ -425,12 +404,6 @@ private static Command CreateLicensesSubcommand(

command.SetHandler(async (config, verbose, dryRun) =>
{
LogDeprecationError(logger, "create-instance licenses");
Environment.Exit(1);

// Unreachable code below - preserved for local development
#pragma warning disable CS0162

if (dryRun)
{
logger.LogInformation("DRY RUN: Adding licenses to Agent User");
Expand All @@ -446,19 +419,13 @@ private static Command CreateLicensesSubcommand(
var instanceConfig = await LoadConfigAsync(logger, configService, config.FullName);
if (instanceConfig == null) Environment.Exit(1);

// Use C# runner with AuthenticationService and GraphApiService
var cleanLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();
var authService = new AuthenticationService(
cleanLoggerFactory.CreateLogger<AuthenticationService>());

var graphService = new GraphApiService(
cleanLoggerFactory.CreateLogger<GraphApiService>(),
executor);
// Use C# runner with the DI-injected GraphApiService (has MSAL auth context)
using var cleanLoggerFactory = LoggerFactoryHelper.CreateCleanLoggerFactory();

var instanceRunner = new A365CreateInstanceRunner(
cleanLoggerFactory.CreateLogger<A365CreateInstanceRunner>(),
executor,
graphService);
graphApiService);

var generatedConfigPath = Path.Combine(
config.DirectoryName ?? Environment.CurrentDirectory,
Expand Down Expand Up @@ -512,24 +479,4 @@ private static Command CreateLicensesSubcommand(
}
}

/// <summary>
/// Logs deprecation error message for create-instance command and its subcommands.
/// Uses plain ASCII text for cross-platform compatibility.
/// </summary>
private static void LogDeprecationError(ILogger logger, string commandName)
{
logger.LogError("ERROR: Command '{Command}' has been deprecated.", commandName);
logger.LogError("");
logger.LogError("This command bypasses the standard agent registration workflow,");
logger.LogError("which prevents proper agent registration and event propagation.");
logger.LogError("");
logger.LogError("Use the recommended workflow instead:");
logger.LogError(" 1. Run 'a365 publish' to package and upload your agent manifest");
logger.LogError(" 2. Run 'a365 deploy' to deploy your application (if Azure-hosted)");
logger.LogError(" 3. Create an agent instance through Microsoft Teams");
logger.LogError("");
logger.LogError("For more information, see:");
logger.LogError("https://learn.microsoft.com/microsoft-agent-365/onboard");
logger.LogError("");
}
}
Loading
Loading