From 706b54a91a6fa853d01d92eec2ed9f85ca444922 Mon Sep 17 00:00:00 2001
From: Copilot <223556219+Copilot@users.noreply.github.com>
Date: Sat, 16 May 2026 15:44:19 +0200
Subject: [PATCH 1/4] Auto-upload test artifacts and build tags to Azure DevOps

Add opt-in Azure DevOps artifact upload support, help/info updates, and unit coverage for the new reporter behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 .../AzureDevOpsArtifactUploader.cs            | 357 ++++++++++++++
 .../AzureDevOpsCommandLineOptions.cs          |   8 +
 .../AzureDevOpsCommandLineProvider.cs         |  79 +++-
 .../AzureDevOpsExtensions.cs                  |  13 +
 .../AzureDevOpsReporter.cs                    |   6 +-
 ...esting.Extensions.AzureDevOpsReport.csproj |   1 +
 .../PathComparison.cs                         |  15 +
 .../Resources/AzureDevOpsResources.resx       |  33 +-
 .../Resources/xlf/AzureDevOpsResources.cs.xlf |  44 +-
 .../Resources/xlf/AzureDevOpsResources.de.xlf |  44 +-
 .../Resources/xlf/AzureDevOpsResources.es.xlf |  44 +-
 .../Resources/xlf/AzureDevOpsResources.fr.xlf |  44 +-
 .../Resources/xlf/AzureDevOpsResources.it.xlf |  44 +-
 .../Resources/xlf/AzureDevOpsResources.ja.xlf |  44 +-
 .../Resources/xlf/AzureDevOpsResources.ko.xlf |  44 +-
 .../Resources/xlf/AzureDevOpsResources.pl.xlf |  44 +-
 .../xlf/AzureDevOpsResources.pt-BR.xlf        |  44 +-
 .../Resources/xlf/AzureDevOpsResources.ru.xlf |  44 +-
 .../Resources/xlf/AzureDevOpsResources.tr.xlf |  44 +-
 .../xlf/AzureDevOpsResources.zh-Hans.xlf      |  44 +-
 .../xlf/AzureDevOpsResources.zh-Hant.xlf      |  44 +-
 .../TargetFrameworkMonikerHelper.cs           |  14 +
 .../HelpInfoAllExtensionsTests.cs             |  42 ++
 .../AzureDevOpsArtifactUploaderTests.cs       | 447 ++++++++++++++++++
 24 files changed, 1540 insertions(+), 47 deletions(-)
 create mode 100644 src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsArtifactUploader.cs
 create mode 100644 src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PathComparison.cs
 create mode 100644 src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/TargetFrameworkMonikerHelper.cs
 create mode 100644 test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs

diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsArtifactUploader.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsArtifactUploader.cs
new file mode 100644
index 0000000000..affb688712
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsArtifactUploader.cs
@@ -0,0 +1,357 @@
+﻿// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.FileSystemGlobbing;
+using Microsoft.Testing.Extensions.AzureDevOpsReport.Resources;
+using Microsoft.Testing.Extensions.Reporting;
+using Microsoft.Testing.Platform;
+using Microsoft.Testing.Platform.CommandLine;
+using Microsoft.Testing.Platform.Configurations;
+using Microsoft.Testing.Platform.Extensions;
+using Microsoft.Testing.Platform.Extensions.Messages;
+using Microsoft.Testing.Platform.Extensions.OutputDevice;
+using Microsoft.Testing.Platform.Extensions.TestHost;
+using Microsoft.Testing.Platform.Helpers;
+using Microsoft.Testing.Platform.Logging;
+using Microsoft.Testing.Platform.OutputDevice;
+using Microsoft.Testing.Platform.Services;
+
+namespace Microsoft.Testing.Extensions.AzureDevOpsReport;
+
+internal sealed class AzureDevOpsArtifactUploader : IDataConsumer, ITestSessionLifetimeHandler, IOutputDeviceDataProducer
+{
+    private const string AzureDevOpsArtifactUploadCommandFormat = "##vso[artifact.upload containerfolder={0};artifactname={0}]{1}";
+    private const string AzureDevOpsBuildAddTagCommandPrefix = "##vso[build.addbuildtag]";
+    private const string AzureDevOpsTfBuildVariableName = "TF_BUILD";
+    private const string CrashDumpProducerUid = "CrashDumpProcessLifetimeHandler";
+    private const string CrashDumpTag = "has-crashdump";
+    private const string HangDumpProducerUid = "HangDumpProcessLifetimeHandler";
+    private const string HangDumpTag = "has-hangdump";
+    private const string TestFailuresTag = "has-test-failures";
+    private static readonly string[] DefaultIncludePatterns = ["**/*"];
+
+    private readonly IConfiguration _configuration;
+    private readonly IEnvironment _environment;
+    private readonly IFileSystem _fileSystem;
+    private readonly IOutputDevice _outputDevice;
+    private readonly ITestApplicationModuleInfo _testApplicationModuleInfo;
+    private readonly ILogger _logger;
+    private readonly AzureDevOpsArtifactUploadMode _uploadMode;
+    private readonly string[] _includePatterns;
+    private readonly string[] _excludePatterns;
+    private readonly string? _artifactNameOverride;
+    private readonly Lazy<string> _targetFrameworkMoniker;
+
+    private bool _emitAzureDevOpsCommands;
+    private int _hasCrashDump;
+    private int _hasHangDump;
+    private int _hasTestFailures;
+    private string? _testResultsDirectory;
+
+    public AzureDevOpsArtifactUploader(
+        ICommandLineOptions commandLineOptions,
+        IConfiguration configuration,
+        IEnvironment environment,
+        IFileSystem fileSystem,
+        IOutputDevice outputDevice,
+        ITestApplicationModuleInfo testApplicationModuleInfo,
+        ILoggerFactory loggerFactory)
+    {
+        _configuration = configuration;
+        _environment = environment;
+        _fileSystem = fileSystem;
+        _outputDevice = outputDevice;
+        _testApplicationModuleInfo = testApplicationModuleInfo;
+        _logger = loggerFactory.CreateLogger<AzureDevOpsArtifactUploader>();
+        _uploadMode = GetUploadMode(commandLineOptions);
+        _includePatterns = GetPatterns(commandLineOptions, AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude, DefaultIncludePatterns);
+        _excludePatterns = GetPatterns(commandLineOptions, AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude, []);
+        _artifactNameOverride = commandLineOptions.TryGetOptionArgumentList(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName, out string[]? artifactNameArguments)
+            && artifactNameArguments is [string artifactName]
+                ? artifactName
+                : null;
+        _targetFrameworkMoniker = new(TargetFrameworkMonikerHelper.GetTargetFrameworkMoniker);
+    }
+
+    public Type[] DataTypesConsumed { get; } = [typeof(TestNodeUpdateMessage), typeof(FileArtifact)];
+
+    public string Uid => nameof(AzureDevOpsArtifactUploader);
+
+    public string Version => ExtensionVersion.DefaultSemVer;
+
+    public string DisplayName => AzureDevOpsResources.DisplayName;
+
+    public string Description => AzureDevOpsResources.Description;
+
+    public Task<bool> IsEnabledAsync() => Task.FromResult(_uploadMode is not AzureDevOpsArtifactUploadMode.Off);
+
+    public async Task OnTestSessionStartingAsync(ITestSessionContext testSessionContext)
+    {
+        try
+        {
+            testSessionContext.CancellationToken.ThrowIfCancellationRequested();
+
+            string? configuredTestResultsDirectory = _configuration.GetTestResultDirectory();
+            _testResultsDirectory = RoslynString.IsNullOrWhiteSpace(configuredTestResultsDirectory)
+                ? null
+                : Path.GetFullPath(configuredTestResultsDirectory);
+            _emitAzureDevOpsCommands = false;
+            Volatile.Write(ref _hasCrashDump, 0);
+            Volatile.Write(ref _hasHangDump, 0);
+            Volatile.Write(ref _hasTestFailures, 0);
+
+            if (_uploadMode is AzureDevOpsArtifactUploadMode.Off)
+            {
+                return;
+            }
+
+            _emitAzureDevOpsCommands = string.Equals(_environment.GetEnvironmentVariable(AzureDevOpsTfBuildVariableName), "true", StringComparison.OrdinalIgnoreCase);
+            if (_emitAzureDevOpsCommands)
+            {
+                return;
+            }
+
+            if (_logger.IsEnabled(LogLevel.Warning))
+            {
+                _logger.LogWarning(AzureDevOpsResources.ArtifactUploadRequiresTfBuildWarning);
+            }
+
+            await _outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(AzureDevOpsResources.ArtifactUploadRequiresTfBuildWarning), testSessionContext.CancellationToken).ConfigureAwait(false);
+        }
+        catch (OperationCanceledException)
+        {
+            throw;
+        }
+        catch (Exception ex)
+        {
+            LogUnexpectedException(nameof(OnTestSessionStartingAsync), ex);
+        }
+    }
+
+    public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken)
+    {
+        try
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+
+            switch (value)
+            {
+                case TestNodeUpdateMessage nodeUpdateMessage when IsFailureState(nodeUpdateMessage.TestNode.Properties.SingleOrDefault<TestNodeStateProperty>()):
+                    Interlocked.Exchange(ref _hasTestFailures, 1);
+                    break;
+
+                case FileArtifact:
+                    TrackDump(dataProducer.Uid);
+                    break;
+            }
+        }
+        catch (OperationCanceledException)
+        {
+            throw;
+        }
+        catch (Exception ex)
+        {
+            LogUnexpectedException(nameof(ConsumeAsync), ex);
+        }
+
+        return Task.CompletedTask;
+    }
+
+    public async Task OnTestSessionFinishingAsync(ITestSessionContext testSessionContext)
+    {
+        try
+        {
+            testSessionContext.CancellationToken.ThrowIfCancellationRequested();
+
+            if (!_emitAzureDevOpsCommands)
+            {
+                return;
+            }
+
+            if (_uploadMode is AzureDevOpsArtifactUploadMode.TagsOnly or AzureDevOpsArtifactUploadMode.All)
+            {
+                if (Volatile.Read(ref _hasCrashDump) == 1)
+                {
+                    await EmitBuildTagAsync(CrashDumpTag, testSessionContext.CancellationToken).ConfigureAwait(false);
+                }
+
+                if (Volatile.Read(ref _hasHangDump) == 1)
+                {
+                    await EmitBuildTagAsync(HangDumpTag, testSessionContext.CancellationToken).ConfigureAwait(false);
+                }
+
+                if (Volatile.Read(ref _hasTestFailures) == 1)
+                {
+                    await EmitBuildTagAsync(TestFailuresTag, testSessionContext.CancellationToken).ConfigureAwait(false);
+                }
+            }
+
+            if (_uploadMode is AzureDevOpsArtifactUploadMode.Files or AzureDevOpsArtifactUploadMode.All)
+            {
+                await EmitArtifactUploadCommandsAsync(testSessionContext.CancellationToken).ConfigureAwait(false);
+            }
+        }
+        catch (OperationCanceledException)
+        {
+            throw;
+        }
+        catch (Exception ex)
+        {
+            LogUnexpectedException(nameof(OnTestSessionFinishingAsync), ex);
+        }
+    }
+
+    private async Task EmitArtifactUploadCommandsAsync(CancellationToken cancellationToken)
+    {
+        try
+        {
+            if (_testResultsDirectory is null || !_fileSystem.ExistDirectory(_testResultsDirectory))
+            {
+                return;
+            }
+
+            string[] files = _fileSystem.GetFiles(_testResultsDirectory, "*", SearchOption.AllDirectories);
+            if (files.Length == 0)
+            {
+                return;
+            }
+
+            Matcher? matcher = ShouldUploadAllFiles() ? null : BuildMatcher();
+            string artifactName = AzDoEscaper.Escape(GetArtifactName());
+            string testResultsDirectoryWithSeparator = EnsureTrailingDirectorySeparator(_testResultsDirectory);
+
+            foreach (string filePath in files.OrderBy(path => path, PathComparison.Comparer))
+            {
+                string? relativePath = TryGetRelativePath(filePath, testResultsDirectoryWithSeparator);
+                if (relativePath is null)
+                {
+                    continue;
+                }
+
+                if (matcher is not null && !MatcherExtensions.Match(matcher, NormalizePath(relativePath)).HasMatches)
+                {
+                    continue;
+                }
+
+                await EmitArtifactUploadCommandAsync(artifactName, filePath, cancellationToken).ConfigureAwait(false);
+            }
+        }
+        catch (OperationCanceledException)
+        {
+            throw;
+        }
+        catch (Exception ex)
+        {
+            LogUnexpectedException(nameof(EmitArtifactUploadCommandsAsync), ex);
+        }
+    }
+
+    private async Task EmitArtifactUploadCommandAsync(string artifactName, string filePath, CancellationToken cancellationToken)
+        => await EmitLineAsync(string.Format(CultureInfo.InvariantCulture, AzureDevOpsArtifactUploadCommandFormat, artifactName, AzDoEscaper.Escape(filePath)), cancellationToken).ConfigureAwait(false);
+
+    private async Task EmitBuildTagAsync(string tag, CancellationToken cancellationToken)
+        => await EmitLineAsync($"{AzureDevOpsBuildAddTagCommandPrefix}{tag}", cancellationToken).ConfigureAwait(false);
+
+    private async Task EmitLineAsync(string line, CancellationToken cancellationToken)
+        => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData(line), cancellationToken).ConfigureAwait(false);
+
+    private string GetArtifactName()
+        => _artifactNameOverride is { } artifactName && !RoslynString.IsNullOrWhiteSpace(artifactName)
+            ? artifactName
+            : $"TestResults_{_testApplicationModuleInfo.TryGetAssemblyName() ?? "unknown"}_{_targetFrameworkMoniker.Value}";
+
+    private Matcher BuildMatcher()
+    {
+        var matcher = new Matcher(PathComparison.Comparison);
+        foreach (string includePattern in _includePatterns)
+        {
+            matcher.AddInclude(includePattern);
+        }
+
+        foreach (string excludePattern in _excludePatterns)
+        {
+            matcher.AddExclude(excludePattern);
+        }
+
+        return matcher;
+    }
+
+    private void LogUnexpectedException(string callbackName, Exception ex)
+    {
+        if (_logger.IsEnabled(LogLevel.Warning))
+        {
+            _logger.LogWarning($"Unexpected exception in {callbackName}: {ex}");
+        }
+    }
+
+    private bool ShouldUploadAllFiles()
+        => _includePatterns.Length == 1
+            && _includePatterns[0] == DefaultIncludePatterns[0]
+            && _excludePatterns.Length == 0;
+
+    private void TrackDump(string dataProducerUid)
+    {
+        switch (dataProducerUid)
+        {
+            case CrashDumpProducerUid:
+                Volatile.Write(ref _hasCrashDump, 1);
+                break;
+
+            case HangDumpProducerUid:
+                Volatile.Write(ref _hasHangDump, 1);
+                break;
+        }
+    }
+
+    private static AzureDevOpsArtifactUploadMode GetUploadMode(ICommandLineOptions commandLineOptions)
+        => commandLineOptions.TryGetOptionArgumentList(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts, out string[]? arguments)
+            && arguments is [string argument]
+                ? ParseUploadMode(argument)
+                : AzureDevOpsArtifactUploadMode.Off;
+
+    private static string[] GetPatterns(ICommandLineOptions commandLineOptions, string optionName, string[] defaultPatterns)
+        => commandLineOptions.TryGetOptionArgumentList(optionName, out string[]? patterns)
+            && patterns is { Length: > 0 }
+                ? [.. patterns.Select(NormalizePath)]
+                : defaultPatterns;
+
+    private static AzureDevOpsArtifactUploadMode ParseUploadMode(string mode)
+        => mode.ToLowerInvariant() switch
+        {
+            AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeTagsOnly => AzureDevOpsArtifactUploadMode.TagsOnly,
+            AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles => AzureDevOpsArtifactUploadMode.Files,
+            AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeAll => AzureDevOpsArtifactUploadMode.All,
+            _ => AzureDevOpsArtifactUploadMode.Off,
+        };
+
+    private static bool IsFailureState(TestNodeStateProperty? state)
+    {
+        if (state is FailedTestNodeStateProperty or ErrorTestNodeStateProperty or TimeoutTestNodeStateProperty)
+        {
+            return true;
+        }
+
+#pragma warning disable CS0618, MTP0001 // Type or member is obsolete
+        return state is CancelledTestNodeStateProperty;
+#pragma warning restore CS0618, MTP0001 // Type or member is obsolete
+    }
+
+    private static string EnsureTrailingDirectorySeparator(string path)
+        => path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar;
+
+    private static string NormalizePath(string path)
+        => path.Replace(Path.DirectorySeparatorChar, '/').Replace(Path.AltDirectorySeparatorChar, '/');
+
+    private static string? TryGetRelativePath(string filePath, string testResultsDirectoryWithSeparator)
+        => filePath.StartsWith(testResultsDirectoryWithSeparator, PathComparison.Comparison)
+            ? filePath.Substring(testResultsDirectoryWithSeparator.Length)
+            : null;
+}
+
+internal enum AzureDevOpsArtifactUploadMode
+{
+    Off,
+    TagsOnly,
+    Files,
+    All,
+}
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineOptions.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineOptions.cs
index bfe0b50025..01aacd3377 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineOptions.cs
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineOptions.cs
@@ -7,4 +7,12 @@ internal static class AzureDevOpsCommandLineOptions
 {
     public const string AzureDevOpsOptionName = "report-azdo";
     public const string AzureDevOpsReportSeverity = "report-azdo-severity";
+    public const string AzureDevOpsUploadArtifactExclude = "report-azdo-upload-artifact-exclude";
+    public const string AzureDevOpsUploadArtifactInclude = "report-azdo-upload-artifact-include";
+    public const string AzureDevOpsUploadArtifactName = "report-azdo-upload-artifact-name";
+    public const string AzureDevOpsUploadArtifacts = "report-azdo-upload-artifacts";
+    public const string AzureDevOpsUploadArtifactsModeAll = "all";
+    public const string AzureDevOpsUploadArtifactsModeFiles = "files";
+    public const string AzureDevOpsUploadArtifactsModeOff = "off";
+    public const string AzureDevOpsUploadArtifactsModeTagsOnly = "tags-only";
 }
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineProvider.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineProvider.cs
index 85598bc581..33309f3ce9 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineProvider.cs
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineProvider.cs
@@ -1,7 +1,10 @@
 ﻿// Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
+using Microsoft.Extensions.FileSystemGlobbing;
+using Microsoft.Testing.Extensions.AzureDevOpsReport;
 using Microsoft.Testing.Extensions.AzureDevOpsReport.Resources;
+using Microsoft.Testing.Platform;
 using Microsoft.Testing.Platform.CommandLine;
 using Microsoft.Testing.Platform.Extensions;
 using Microsoft.Testing.Platform.Extensions.CommandLine;
@@ -10,6 +13,14 @@ namespace Microsoft.Testing.Extensions.Reporting;
 
 internal sealed class AzureDevOpsCommandLineProvider : ICommandLineOptionsProvider
 {
+    private static readonly string[] ArtifactUploadModes =
+    [
+        AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeOff,
+        AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeTagsOnly,
+        AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles,
+        AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeAll,
+    ];
+
     private static readonly string[] SeverityOptions = ["error", "warning"];
 
     public string Uid => nameof(AzureDevOpsCommandLineProvider);
@@ -27,30 +38,70 @@ public IReadOnlyCollection<CommandLineOption> GetCommandLineOptions()
         [
             new CommandLineOption(AzureDevOpsCommandLineOptions.AzureDevOpsOptionName, AzureDevOpsResources.OptionDescription, ArgumentArity.Zero, false),
             new CommandLineOption(AzureDevOpsCommandLineOptions.AzureDevOpsReportSeverity, AzureDevOpsResources.SeverityOptionDescription, ArgumentArity.ExactlyOne, false),
+            new CommandLineOption(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude, AzureDevOpsResources.UploadArtifactExcludeOptionDescription, ArgumentArity.ZeroOrMore, false),
+            new CommandLineOption(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude, AzureDevOpsResources.UploadArtifactIncludeOptionDescription, ArgumentArity.ZeroOrMore, false),
+            new CommandLineOption(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName, AzureDevOpsResources.UploadArtifactNameOptionDescription, ArgumentArity.ExactlyOne, false),
+            new CommandLineOption(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts, AzureDevOpsResources.UploadArtifactsOptionDescription, ArgumentArity.ExactlyOne, false),
         ];
 
     public Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments)
-    {
-        if (commandOption.Name == AzureDevOpsCommandLineOptions.AzureDevOpsReportSeverity)
+        => commandOption.Name switch
         {
-            if (!SeverityOptions.Contains(arguments[0], StringComparer.OrdinalIgnoreCase))
-            {
-                return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, AzureDevOpsResources.InvalidSeverity, arguments[0]));
-            }
-        }
-
-        return ValidationResult.ValidTask;
-    }
+            AzureDevOpsCommandLineOptions.AzureDevOpsReportSeverity when !SeverityOptions.Contains(arguments[0], StringComparer.OrdinalIgnoreCase)
+                => ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, AzureDevOpsResources.InvalidSeverity, arguments[0])),
+            AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts when !ArtifactUploadModes.Contains(arguments[0], StringComparer.OrdinalIgnoreCase)
+                => ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, AzureDevOpsResources.InvalidArtifactUploadMode, arguments[0])),
+            AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude or AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude
+                => ValidateGlobPatternsAsync(arguments),
+            _ => ValidationResult.ValidTask,
+        };
 
     public Task<ValidationResult> ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions)
+        => !commandLineOptions.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsOptionName)
+            && commandLineOptions.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsReportSeverity)
+                ? ValidationResult.InvalidTask(AzureDevOpsResources.AzureDevOpsReportSeverityRequiresAzureDevOps)
+                : HasArtifactUploadConfiguration(commandLineOptions) && IsArtifactUploadDisabled(commandLineOptions)
+                    ? ValidationResult.InvalidTask(AzureDevOpsResources.ArtifactUploadOptionsRequireUploadArtifacts)
+                    : ValidationResult.ValidTask;
+
+    private static bool HasArtifactUploadConfiguration(ICommandLineOptions commandLineOptions)
+        => commandLineOptions.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude)
+            || commandLineOptions.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude)
+            || commandLineOptions.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName);
+
+    private static bool IsAbsolutePattern(string pattern)
+        => pattern.Length > 0
+            && (pattern[0] is '/' or '\\'
+                || (pattern.Length >= 2 && pattern[1] == ':'));
+
+    private static bool IsArtifactUploadDisabled(ICommandLineOptions commandLineOptions)
+        => !commandLineOptions.TryGetOptionArgumentList(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts, out string[]? arguments)
+            || (arguments is [string argument]
+                && AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeOff.Equals(argument, StringComparison.OrdinalIgnoreCase));
+
+    private static Task<ValidationResult> ValidateGlobPatternsAsync(string[] arguments)
     {
-        if (!commandLineOptions.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsOptionName) &&
-            commandLineOptions.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsReportSeverity))
+        foreach (string argument in arguments)
         {
-            // If report-azdo is not set, but report-azdo-severity is set, it's invalid.
-            return ValidationResult.InvalidTask(AzureDevOpsResources.AzureDevOpsReportSeverityRequiresAzureDevOps);
+            if (RoslynString.IsNullOrWhiteSpace(argument) || IsAbsolutePattern(argument))
+            {
+                return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, AzureDevOpsResources.InvalidArtifactUploadGlob, argument));
+            }
+
+            try
+            {
+                var matcher = new Matcher(PathComparison.Comparison);
+                matcher.AddInclude(NormalizePattern(argument));
+            }
+            catch (ArgumentException)
+            {
+                return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, AzureDevOpsResources.InvalidArtifactUploadGlob, argument));
+            }
         }
 
         return ValidationResult.ValidTask;
     }
+
+    private static string NormalizePattern(string pattern)
+        => pattern.Replace(Path.DirectorySeparatorChar, '/').Replace(Path.AltDirectorySeparatorChar, '/');
 }
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs
index 728a257520..d16eb29910 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs
@@ -29,7 +29,20 @@ public static void AddAzureDevOpsProvider(this ITestApplicationBuilder builder)
                    serviceProvider.GetOutputDevice(),
                    serviceProvider.GetLoggerFactory()));
 
+        var compositeArtifactUploader =
+            new CompositeExtensionFactory<AzureDevOpsArtifactUploader>(serviceProvider =>
+                new AzureDevOpsArtifactUploader(
+                    serviceProvider.GetCommandLineOptions(),
+                    serviceProvider.GetConfiguration(),
+                    serviceProvider.GetEnvironment(),
+                    serviceProvider.GetFileSystem(),
+                    serviceProvider.GetOutputDevice(),
+                    serviceProvider.GetTestApplicationModuleInfo(),
+                    serviceProvider.GetLoggerFactory()));
+
         builder.TestHost.AddDataConsumer(compositeTestSessionAzDoService);
+        builder.TestHost.AddDataConsumer(compositeArtifactUploader);
+        builder.TestHost.AddTestSessionLifetimeHandler(compositeArtifactUploader);
 
         builder.CommandLine.AddProvider(() => new AzureDevOpsCommandLineProvider());
     }
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs
index aebb6b47e8..6379b27e8c 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs
@@ -42,7 +42,7 @@ public AzureDevOpsReporter(
         _fileSystem = fileSystem;
         _outputDisplay = outputDisplay;
         _logger = loggerFactory.CreateLogger<AzureDevOpsReporter>();
-        _targetFrameworkMoniker = GetTargetFrameworkMoniker();
+        _targetFrameworkMoniker = TargetFrameworkMonikerHelper.GetTargetFrameworkMoniker();
     }
 
     public Type[] DataTypesConsumed { get; } =
@@ -307,10 +307,6 @@ private async Task WriteExceptionAsync(string testDisplayName, string? explanati
         return null;
     }
 
-    private static string GetTargetFrameworkMoniker()
-        => TargetFrameworkParser.GetShortTargetFramework(Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkDisplayName)
-            ?? TargetFrameworkParser.GetShortTargetFramework(RuntimeInformation.FrameworkDescription);
-
     private static (string Code, string File, int LineNumber)? GetStackFrameLocation(string stackTraceLine)
     {
         Match match = StackTraceHelper.GetFrameRegex().Match(stackTraceLine);
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj
index 025d2c16f3..7585d91e4d 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj
@@ -48,6 +48,7 @@ This package extends Microsoft Testing Platform to provide a Azure DevOps report
   </ItemGroup>
 
   <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" />
     <ProjectReference Include="$(RepoRoot)src\Platform\Microsoft.Testing.Platform\Microsoft.Testing.Platform.csproj" />
   </ItemGroup>
 
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PathComparison.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PathComparison.cs
new file mode 100644
index 0000000000..ebf97fd1d0
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PathComparison.cs
@@ -0,0 +1,15 @@
+﻿// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.Testing.Extensions.AzureDevOpsReport;
+
+internal static class PathComparison
+{
+    public static StringComparison Comparison { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
+        ? StringComparison.OrdinalIgnoreCase
+        : StringComparison.Ordinal;
+
+    public static StringComparer Comparer { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
+        ? StringComparer.OrdinalIgnoreCase
+        : StringComparer.Ordinal;
+}
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx
index 516d5d4382..48ad43a860 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx
@@ -130,10 +130,39 @@
     <value>Invalid option {0}.</value>
   </data>
   <data name="OptionDescription" xml:space="preserve">
-    <value>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</value>
+    <value>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</value>
   </data>
   <data name="SeverityOptionDescription" xml:space="preserve">
     <value>Severity to use for the reported event. Options are: error (default) and warning.</value>
     <comment>{Locked="error"}{Locked="warning"}</comment>
   </data>
-</root>
\ No newline at end of file
+  <data name="UploadArtifactExcludeOptionDescription" xml:space="preserve">
+    <value>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</value>
+  </data>
+  <data name="UploadArtifactIncludeOptionDescription" xml:space="preserve">
+    <value>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</value>
+    <comment>{Locked="**/*"}</comment>
+  </data>
+  <data name="UploadArtifactNameOptionDescription" xml:space="preserve">
+    <value>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</value>
+    <comment>{Locked="TestResults_{assemblyName}_{tfm}"}</comment>
+  </data>
+  <data name="UploadArtifactsOptionDescription" xml:space="preserve">
+    <value>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</value>
+    <comment>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</comment>
+  </data>
+  <data name="InvalidArtifactUploadGlob" xml:space="preserve">
+    <value>Invalid glob pattern {0}.</value>
+  </data>
+  <data name="InvalidArtifactUploadMode" xml:space="preserve">
+    <value>Invalid artifact upload mode {0}.</value>
+  </data>
+  <data name="ArtifactUploadOptionsRequireUploadArtifacts" xml:space="preserve">
+    <value>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</value>
+    <comment>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</comment>
+  </data>
+  <data name="ArtifactUploadRequiresTfBuildWarning" xml:space="preserve">
+    <value>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</value>
+    <comment>{Locked="TF_BUILD"}{Locked="true"}</comment>
+  </data>
+</root>
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf
index 076a3037b9..6d08a90dbb 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="cs" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">--report-azdo-severity vyžaduje, aby byla povolena možnost --report-azdo</target>
@@ -17,14 +27,24 @@
         <target state="translated">Generátor sestav Azure DevOps</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Neplatná možnost {0}.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Umožňuje generátoru sestav Azure DevOps zapisovat chyby do výstupu způsobem, který je pro AzureDev Ops srozumitelný.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Umožňuje generátoru sestav Azure DevOps zapisovat chyby do výstupu způsobem, který je pro AzureDev Ops srozumitelný.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">Závažnost, která se má použít pro hlášenou událost. Možnosti: error (výchozí) a warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf
index 87be6a6ce5..db9c093cd8 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="de" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">Für „--report-azdo-severity“ muss „--report-azdo“ aktiviert sein.</target>
@@ -17,14 +27,24 @@
         <target state="translated">Azure DevOps-Berichts-Generator</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Ungültige Option „{0}“.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Azure DevOps-Berichts-Generator aktivieren, um Fehler auf eine Weise in die Ausgabe zu schreiben, die AzureDev Ops versteht.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Azure DevOps-Berichts-Generator aktivieren, um Fehler auf eine Weise in die Ausgabe zu schreiben, die AzureDev Ops versteht.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">Schweregrad, der für das gemeldete Ereignis verwendet werden soll. Optionen sind: error (Standard) und warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf
index c4a61c952f..f8ae542667 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="es" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">"--report-azdo-severity" requiere que se habilite "--report-azdo"</target>
@@ -17,14 +27,24 @@
         <target state="translated">Generador de informes de Azure DevOps</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Opción no válida {0}.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Habilite el generador de informes de Azure DevOps para escribir errores en la salida de una manera que AzureDev Ops comprenda.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Habilite el generador de informes de Azure DevOps para escribir errores en la salida de una manera que AzureDev Ops comprenda.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">Gravedad que se va a usar para el evento notificado. Las opciones son: error (valor predeterminado) y warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf
index 4feb6d0ab7..ca4c768ac2 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="fr" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">« --report-azdo-severity » nécessite l’activation de « --report-azdo »</target>
@@ -17,14 +27,24 @@
         <target state="translated">Générateur de rapports Azure DevOps</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Option {0} non valide.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Activez le générateur de rapports Azure DevOps pour écrire les erreurs dans la sortie d’une manière comprise par Azure DevOps.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Activez le générateur de rapports Azure DevOps pour écrire les erreurs dans la sortie d’une manière comprise par Azure DevOps.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">Gravité à utiliser pour l’événement signalé. Les options sont : error (par défaut) et warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf
index d9756f675c..5d457b9f66 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="it" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">'--report-azdo-severity' richiede l'autenticazione di '--report-azdo'</target>
@@ -17,14 +27,24 @@
         <target state="translated">Generatore di report di Azure DevOps</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Opzione non valida {0}.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Abilitare il generatore di report di Azure DevOps per scrivere gli errori nell'output in un modo che Azure DevOps comprenda.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Abilitare il generatore di report di Azure DevOps per scrivere gli errori nell'output in un modo che Azure DevOps comprenda.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">Gravità da usare per l'evento segnalato. Le opzioni sono: error (impostazione predefinita) e warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf
index ea3529945b..48a3a826cb 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="ja" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">'--report-azdo-severity' では、'--report-azdo' を有効にする必要があります</target>
@@ -17,14 +27,24 @@
         <target state="translated">Azure DevOps レポート生成プログラム</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">オプション {0} が無効です。</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Azure DevOps レポート生成プログラムを有効にして、AzureDev Ops が理解する方法で出力にエラーを書き込みます。</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Azure DevOps レポート生成プログラムを有効にして、AzureDev Ops が理解する方法で出力にエラーを書き込みます。</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">報告されたイベントに使用する重大度。オプションは、error (既定) と warning です。</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf
index f74caa7f6e..7ae8f614f4 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="ko" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">'--report-azdo-severity'를 사용하려면 '--report-azdo'를 사용 설정 해야합니다.</target>
@@ -17,14 +27,24 @@
         <target state="translated">Azure DevOps 보고서 생성기</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">{0} 옵션이 잘못되었습니다.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Azure DevOps 보고서 생성기를 활성화하여 Azure DevOps가 이해할 수 있는 방식으로 출력에 오류를 기록합니다.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Azure DevOps 보고서 생성기를 활성화하여 Azure DevOps가 이해할 수 있는 방식으로 출력에 오류를 기록합니다.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">보고된 이벤트에 사용할 심각도입니다. 옵션은 error(기본값)와 warning, 두 가지입니다.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf
index 8890f27f70..9e1cd56299 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="pl" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">Element „--report-azdo-severity” wymaga włączenia polecenia „--report-azdo”</target>
@@ -17,14 +27,24 @@
         <target state="translated">Generator raportów usługi Azure DevOps</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Nieprawidłowa opcja {0}.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Włącz generator raportów usługi Azure DevOps, aby zapisywać błędy w danych wyjściowych w sposób zrozumiały dla usługi Azure DevOps.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Włącz generator raportów usługi Azure DevOps, aby zapisywać błędy w danych wyjściowych w sposób zrozumiały dla usługi Azure DevOps.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">Ważność do użycia dla zgłoszonego zdarzenia. Dostępne opcje to: error (domyślny) i warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf
index d660d0e56a..113bf6d0ce 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="pt-BR" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">'--report-azdo-severity' requer que '--report-azdo' esteja habilitado</target>
@@ -17,14 +27,24 @@
         <target state="translated">Gerador de relatórios do Azure DevOps</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Opção {0} inválida.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Habilitar o gerador de relatórios do Azure DevOps para gravar erros na saída de uma forma que o Azure DevOps entenda.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Habilitar o gerador de relatórios do Azure DevOps para gravar erros na saída de uma forma que o Azure DevOps entenda.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">A gravidade que será usada para o evento relatado. As opções são: error (padrão) e warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf
index 756d7c9cae..5f8b2b229a 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="ru" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">"--report-azdo-severity" требует включения "--report-azdo"</target>
@@ -17,14 +27,24 @@
         <target state="translated">Генератор отчетов Azure DevOps</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Недопустимый параметр {0}.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Включите генератор отчетов Azure DevOps для записи ошибок в выходные данные в формате, который понимает Azure DevOps.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Включите генератор отчетов Azure DevOps для записи ошибок в выходные данные в формате, который понимает Azure DevOps.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">Уровень серьезности, используемый для события из отчета. Параметры: error (по умолчанию) и warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf
index 69207860a2..974f3e0383 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="tr" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">'--report-azdo-severity' öğesi '--report-azdo' öğesinin etkinleştirilmesini gerektirir</target>
@@ -17,14 +27,24 @@
         <target state="translated">Azure DevOps rapor oluşturucusu</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">Geçersiz seçenek{0}.</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">Azure DevOps rapor oluşturucusunu, hataları Azure DevOps'un anlayacağı şekilde çıktıya yazması için etkinleştirin.</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">Azure DevOps rapor oluşturucusunu, hataları Azure DevOps'un anlayacağı şekilde çıktıya yazması için etkinleştirin.</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">Raporlanan olay için kullanılacak önem derecesi. Seçenekler şunlardır: error (varsayılan) ve warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf
index 2b5199f4ad..f0e396e925 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="zh-Hans" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">'--report-azdo-severity' 要求启用 '--report-azdo'</target>
@@ -17,14 +27,24 @@
         <target state="translated">Azure DevOps报表生成器</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">选项 {0} 无效。</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">启用 Azure DevOps 报表生成器，以通过 Azure DevOps 可理解的方式将错误写入输出。</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">启用 Azure DevOps 报表生成器，以通过 Azure DevOps 可理解的方式将错误写入输出。</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">要用于所报告事件的严重性。选项为: error (默认)和 warning。</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf
index 6c61f0fb8f..8560099d19 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="zh-Hant" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsReportSeverityRequiresAzureDevOps">
         <source>'--report-azdo-severity' requires '--report-azdo' to be enabled</source>
         <target state="translated">'--report-azdo-severity' 需要啟用 '--report-azdo'</target>
@@ -17,14 +27,24 @@
         <target state="translated">Azure DevOps 報告產生器</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidSeverity">
         <source>Invalid option {0}.</source>
         <target state="translated">無效的選項 {0}。</target>
         <note />
       </trans-unit>
       <trans-unit id="OptionDescription">
-        <source>Eanble Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
-        <target state="translated">啟用 Azure DevOps 報告產生器，以 Azure DevOps 可理解的方式將錯誤寫入輸出。</target>
+        <source>Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.</source>
+        <target state="needs-review-translation">啟用 Azure DevOps 報告產生器，以 Azure DevOps 可理解的方式將錯誤寫入輸出。</target>
         <note />
       </trans-unit>
       <trans-unit id="SeverityOptionDescription">
@@ -32,6 +52,26 @@
         <target state="translated">要用於所報告事件的嚴重性。選項為: error (預設) warning。</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/TargetFrameworkMonikerHelper.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/TargetFrameworkMonikerHelper.cs
new file mode 100644
index 0000000000..13a8ab500e
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/TargetFrameworkMonikerHelper.cs
@@ -0,0 +1,14 @@
+﻿// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Testing.Platform.OutputDevice;
+
+namespace Microsoft.Testing.Extensions.AzureDevOpsReport;
+
+internal static class TargetFrameworkMonikerHelper
+{
+    public static string GetTargetFrameworkMoniker()
+        => TargetFrameworkParser.GetShortTargetFramework(Assembly.GetEntryAssembly()?.GetCustomAttribute<System.Runtime.Versioning.TargetFrameworkAttribute>()?.FrameworkDisplayName)
+            ?? TargetFrameworkParser.GetShortTargetFramework(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription)
+            ?? "unknown";
+}
diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs
index 7449a51387..0642494267 100644
--- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs
+++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs
@@ -100,6 +100,18 @@ Disable reporting progress to screen.
     --output
         Output verbosity when reporting tests.
         Valid values are 'Normal', 'Detailed'. Default is 'Normal'.
+    --report-azdo
+        Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.
+    --report-azdo-severity
+        Severity to use for the reported event. Options are: error (default) and warning.
+    --report-azdo-upload-artifact-exclude
+        Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.
+    --report-azdo-upload-artifact-include
+        Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.
+    --report-azdo-upload-artifact-name
+        Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.
+    --report-azdo-upload-artifacts
+        Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.
     --report-trx
         Enable generating TRX report
     --report-trx-filename
@@ -265,6 +277,35 @@ Note that this is slowing down the test execution.
         Description: A global test execution timeout.
         Takes one argument as string in the format <value>[h|m|s] where 'value' is float.
 Registered command line providers:
+  AzureDevOpsCommandLineProvider
+    Name: Azure DevOps report generator
+    Version: *
+    Description: Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.
+    Options:
+      --report-azdo
+        Arity: 0
+        Hidden: False
+        Description: Enable Azure DevOps report generator to write errors to the output in a way that AzureDev Ops understands.
+      --report-azdo-severity
+        Arity: 1
+        Hidden: False
+        Description: Severity to use for the reported event. Options are: error (default) and warning.
+      --report-azdo-upload-artifact-exclude
+        Arity: 0..N
+        Hidden: False
+        Description: Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.
+      --report-azdo-upload-artifact-include
+        Arity: 0..N
+        Hidden: False
+        Description: Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.
+      --report-azdo-upload-artifact-name
+        Arity: 1
+        Hidden: False
+        Description: Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.
+      --report-azdo-upload-artifacts
+        Arity: 1
+        Hidden: False
+        Description: Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.
   CrashDumpCommandLineProvider
     Name: Crash dump
     Version: *
@@ -431,6 +472,7 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase()
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="Microsoft.Testing.Platform.MSBuild" Version="$MicrosoftTestingPlatformVersion$" />
+        <PackageReference Include="Microsoft.Testing.Extensions.AzureDevOpsReport" Version="$MicrosoftTestingPlatformVersion$" />
         <PackageReference Include="Microsoft.Testing.Extensions.CrashDump" Version="$MicrosoftTestingPlatformVersion$" />
         <PackageReference Include="Microsoft.Testing.Extensions.HangDump" Version="$MicrosoftTestingPlatformVersion$" />
         <PackageReference Include="Microsoft.Testing.Extensions.HotReload" Version="$MicrosoftTestingPlatformVersion$" />
diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs
new file mode 100644
index 0000000000..18d8d1989d
--- /dev/null
+++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs
@@ -0,0 +1,447 @@
+﻿// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Testing.Extensions.AzureDevOpsReport;
+using Microsoft.Testing.Extensions.Reporting;
+using Microsoft.Testing.Extensions.UnitTests.Helpers;
+using Microsoft.Testing.Platform.CommandLine;
+using Microsoft.Testing.Platform.Configurations;
+using Microsoft.Testing.Platform.Extensions.Messages;
+using Microsoft.Testing.Platform.Extensions.OutputDevice;
+using Microsoft.Testing.Platform.Helpers;
+using Microsoft.Testing.Platform.Logging;
+using Microsoft.Testing.Platform.OutputDevice;
+using Microsoft.Testing.Platform.Services;
+using Microsoft.Testing.Platform.TestHost;
+
+using Moq;
+
+namespace Microsoft.Testing.Extensions.UnitTests;
+
+[TestClass]
+public sealed class AzureDevOpsArtifactUploaderTests
+{
+    private const string ArtifactUploadOptionsRequireUploadArtifactsMessage = "'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.";
+    private const string ResultsDirectory = @"Q:\results";
+
+    private readonly Mock<IConfiguration> _configurationMock = new();
+    private readonly Mock<IEnvironment> _environmentMock = new();
+    private readonly Mock<IFileSystem> _fileSystemMock = new();
+    private readonly Mock<IOutputDevice> _outputDeviceMock = new();
+    private readonly Mock<ITestApplicationModuleInfo> _testApplicationModuleInfoMock = new();
+    private readonly Mock<ILoggerFactory> _loggerFactoryMock = new();
+    private readonly List<IOutputDeviceData> _outputData = [];
+
+    public AzureDevOpsArtifactUploaderTests()
+    {
+        _ = _configurationMock.SetupGet(configuration => configuration[PlatformConfigurationConstants.PlatformResultDirectory]).Returns(ResultsDirectory);
+        _ = _testApplicationModuleInfoMock.Setup(testApplicationModuleInfo => testApplicationModuleInfo.TryGetAssemblyName()).Returns("MyAssembly");
+        _ = _loggerFactoryMock.Setup(loggerFactory => loggerFactory.CreateLogger(It.IsAny<string>())).Returns(Mock.Of<ILogger>());
+        _ = _outputDeviceMock
+            .Setup(outputDevice => outputDevice.DisplayAsync(It.IsAny<IOutputDeviceDataProducer>(), It.IsAny<IOutputDeviceData>(), It.IsAny<CancellationToken>()))
+            .Callback<IOutputDeviceDataProducer, IOutputDeviceData, CancellationToken>((_, data, _) => _outputData.Add(data))
+            .Returns(Task.CompletedTask);
+    }
+
+    [TestMethod]
+    public async Task OffMode_IsDisabledAndNoOps()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader([]);
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
+            .Returns([@"Q:\results\test.trx"]);
+
+        Assert.IsFalse(await uploader.IsEnabledAsync().ConfigureAwait(false));
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        Assert.IsEmpty(GetFormattedLines());
+        Assert.IsEmpty(GetWarnings());
+    }
+
+    [TestMethod]
+    public async Task TagsOnlyMode_EmitsOnlyBuildTags()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeTagsOnly],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\crashdump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("HangDumpProcessLifetimeHandler", "Hang dump"), CreateFileArtifact(@"Q:\results\hang\hangdump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        CollectionAssert.AreEquivalent(
+            new[]
+            {
+                "##vso[build.addbuildtag]has-crashdump",
+                "##vso[build.addbuildtag]has-hangdump",
+                "##vso[build.addbuildtag]has-test-failures",
+            },
+            GetFormattedLines());
+        Assert.DoesNotContain(line => line.Contains("artifact.upload", StringComparison.Ordinal), GetFormattedLines());
+        Assert.IsEmpty(GetWarnings());
+    }
+
+    [TestMethod]
+    public async Task FilesMode_EmitsOnlyArtifactLines()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName] = ["Artifacts"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
+            .Returns([@"Q:\results\a.trx", @"Q:\results\b.dmp"]);
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        string[] lines = GetFormattedLines();
+        Assert.HasCount(2, lines);
+        Assert.IsTrue(lines.All(line => line.StartsWith("##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]", StringComparison.Ordinal)));
+        Assert.DoesNotContain(line => line.Contains("build.addbuildtag", StringComparison.Ordinal), lines);
+    }
+
+    [TestMethod]
+    public async Task FilesMode_SkipsArtifactsOutsideResultsDirectory()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName] = ["Artifacts"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
+            .Returns([@"Q:\results\inside.trx", @"Q:\results-other\outside.trx"]);
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        CollectionAssert.AreEqual(
+            new[] { @"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]Q:\results\inside.trx" },
+            GetFormattedLines());
+    }
+
+    [TestMethod]
+    public async Task AllMode_EmitsBuildTagsAndArtifactLines()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName] = ["Artifacts"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeAll],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
+            .Returns([@"Q:\results\test.trx"]);
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        CollectionAssert.AreEqual(
+            new[]
+            {
+                "##vso[build.addbuildtag]has-crashdump",
+                "##vso[build.addbuildtag]has-test-failures",
+                @"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]Q:\results\test.trx",
+            },
+            GetFormattedLines());
+    }
+
+    [TestMethod]
+    public async Task IncludeAndExcludeGlobs_AreAppliedToArtifactUploads()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude] = ["skip\\**"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude] = ["**\\*.trx"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName] = ["Artifacts"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
+            .Returns([@"Q:\results\keep.trx", @"Q:\results\keep.coverage", @"Q:\results\skip\ignored.trx"]);
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        CollectionAssert.AreEqual(
+            new[] { @"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]Q:\results\keep.trx" },
+            GetFormattedLines());
+    }
+
+    [TestMethod]
+    public async Task ConsumedArtifacts_DetectCrashAndHangDumps()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeTagsOnly],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("HangDumpProcessLifetimeHandler", "Hang dump"), CreateFileArtifact(@"Q:\results\hang\dump.log"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        CollectionAssert.AreEquivalent(
+            new[]
+            {
+                "##vso[build.addbuildtag]has-crashdump",
+                "##vso[build.addbuildtag]has-hangdump",
+            },
+            GetFormattedLines());
+    }
+
+    [TestMethod]
+    public async Task NonDumpProducer_DoesNotEmitDumpTags()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeTagsOnly],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("GenericProducer", "Generic"), CreateFileArtifact(@"Q:\results\hang\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        Assert.IsEmpty(GetFormattedLines());
+    }
+
+    [TestMethod]
+    public async Task HasTestFailuresTag_IsNotEmittedWithoutFailures()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeTagsOnly],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer(), CreatePassedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        Assert.DoesNotContain("##vso[build.addbuildtag]has-test-failures", GetFormattedLines());
+    }
+
+    [TestMethod]
+    public async Task MissingTfBuild_EmitsWarningAndSkipsOutput()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeAll],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns((string?)null);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
+            .Returns([@"Q:\results\test.trx"]);
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        CollectionAssert.AreEqual(
+            new[] { "Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags." },
+            GetWarnings());
+        Assert.IsEmpty(GetFormattedLines());
+    }
+
+    [TestMethod]
+    public async Task EmptyTestResultsDirectory_DoesNotEmitArtifactLines()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles],
+        });
+
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories)).Returns([]);
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        Assert.IsEmpty(GetFormattedLines());
+        Assert.IsEmpty(GetWarnings());
+    }
+
+    [TestMethod]
+    public async Task WhitespaceTestResultsDirectory_DoesNotEmitArtifactLines()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles],
+        });
+
+        _ = _configurationMock.SetupGet(configuration => configuration[PlatformConfigurationConstants.PlatformResultDirectory]).Returns("   ");
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        Assert.IsEmpty(GetFormattedLines());
+        Assert.IsEmpty(GetWarnings());
+    }
+
+    [TestMethod]
+    public async Task ArtifactUploadPaths_AreEscaped()
+    {
+        AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
+        {
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName] = ["Artifacts"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles],
+        });
+
+        string specialPath = "Q:\\results\\semi;line\r\nname.trx";
+        _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
+        _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories)).Returns([specialPath]);
+
+        await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
+        await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
+
+        CollectionAssert.AreEqual(
+            new[] { "##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]Q:\\results\\semi%3Bline%0D%0Aname.trx" },
+            GetFormattedLines());
+    }
+
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude, @"C:\absolute\*.trx")]
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude, "/absolute/*.trx")]
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude, @"\absolute\*.trx")]
+    [TestMethod]
+    public async Task ValidateOptionArgumentsAsync_RejectsAbsoluteGlobPatterns(string optionName, string pattern)
+    {
+        var provider = new AzureDevOpsCommandLineProvider();
+        Microsoft.Testing.Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions().Single(commandLineOption => commandLineOption.Name == optionName);
+
+        ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [pattern]).ConfigureAwait(false);
+
+        Assert.IsFalse(result.IsValid);
+        Assert.AreEqual($"Invalid glob pattern {pattern}.", result.ErrorMessage);
+    }
+
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude, false)]
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude, false)]
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName, false)]
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude, true)]
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude, true)]
+    [DataRow(AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName, true)]
+    [TestMethod]
+    public async Task ValidateCommandLineOptionsAsync_RejectsArtifactSettingsWhenUploadsAreDisabled(string optionName, bool setOffMode)
+    {
+        var provider = new AzureDevOpsCommandLineProvider();
+        var options = new Dictionary<string, string[]>
+        {
+            [optionName] = optionName == AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName ? ["Artifacts"] : ["**/*.trx"],
+        };
+
+        if (setOffMode)
+        {
+            options[AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeOff];
+        }
+
+        ValidationResult result = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false);
+
+        Assert.IsFalse(result.IsValid);
+        Assert.AreEqual(ArtifactUploadOptionsRequireUploadArtifactsMessage, result.ErrorMessage);
+    }
+
+    [TestMethod]
+    public async Task ValidateOptionArgumentsAsync_RejectsUnknownUploadMode()
+    {
+        var provider = new AzureDevOpsCommandLineProvider();
+        Microsoft.Testing.Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions().Single(commandLineOption => commandLineOption.Name == AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts);
+
+        ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, ["unexpected"]).ConfigureAwait(false);
+
+        Assert.IsFalse(result.IsValid);
+        Assert.AreEqual("Invalid artifact upload mode unexpected.", result.ErrorMessage);
+    }
+
+    private AzureDevOpsArtifactUploader CreateUploader(Dictionary<string, string[]> options)
+        => new(
+            new TestCommandLineOptions(options),
+            _configurationMock.Object,
+            _environmentMock.Object,
+            _fileSystemMock.Object,
+            _outputDeviceMock.Object,
+            _testApplicationModuleInfoMock.Object,
+            _loggerFactoryMock.Object);
+
+    private static FileArtifact CreateFileArtifact(string path)
+        => new(new FileInfo(path), "Artifact");
+
+    private static TestNodeUpdateMessage CreateFailedTestNodeUpdateMessage()
+        => CreateTestNodeUpdateMessage(new FailedTestNodeStateProperty());
+
+    private static TestNodeUpdateMessage CreatePassedTestNodeUpdateMessage()
+        => CreateTestNodeUpdateMessage(new PassedTestNodeStateProperty());
+
+    private static TestNodeUpdateMessage CreateTestNodeUpdateMessage(TestNodeStateProperty state)
+        => new(
+            new SessionUid("session"),
+            new TestNode
+            {
+                Uid = "test",
+                DisplayName = "TestDisplayName",
+                Properties = new PropertyBag(state),
+            });
+
+    private static IDataProducer CreateProducer(string uid = "Producer", string displayName = "Producer")
+        => new TestProducer(uid, displayName);
+
+    private string[] GetFormattedLines()
+        => [.. _outputData.OfType<FormattedTextOutputDeviceData>().Select(output => output.Text)];
+
+    private string[] GetWarnings()
+        => [.. _outputData.OfType<WarningMessageOutputDeviceData>().Select(output => output.Message)];
+
+    private sealed class TestProducer(string uid, string displayName) : IDataProducer
+    {
+        public Type[] DataTypesProduced { get; } = [typeof(FileArtifact)];
+
+        public string Uid { get; } = uid;
+
+        public string Version { get; } = "1.0.0";
+
+        public string DisplayName { get; } = displayName;
+
+        public string Description { get; } = displayName;
+
+        public Task<bool> IsEnabledAsync() => Task.FromResult(true);
+    }
+
+    private sealed class TestSessionContext : ITestSessionContext
+    {
+        public SessionUid SessionUid { get; } = new("session");
+
+        public CancellationToken CancellationToken { get; } = CancellationToken.None;
+    }
+}

From 79f309ac69d69ee451aa705d5528549dbb0edc31 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 18 May 2026 07:19:59 +0000
Subject: [PATCH 2/4] Make artifact uploader tests path-portable

Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
---
 .../AzureDevOpsArtifactUploaderTests.cs       | 51 +++++++++++--------
 1 file changed, 29 insertions(+), 22 deletions(-)

diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs
index 18d8d1989d..393565aa0a 100644
--- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs
+++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs
@@ -22,7 +22,7 @@ namespace Microsoft.Testing.Extensions.UnitTests;
 public sealed class AzureDevOpsArtifactUploaderTests
 {
     private const string ArtifactUploadOptionsRequireUploadArtifactsMessage = "'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.";
-    private const string ResultsDirectory = @"Q:\results";
+    private static readonly string ResultsDirectory = Path.Combine(Path.GetTempPath(), "azdo-artifact-uploader-tests");
 
     private readonly Mock<IConfiguration> _configurationMock = new();
     private readonly Mock<IEnvironment> _environmentMock = new();
@@ -51,12 +51,12 @@ public async Task OffMode_IsDisabledAndNoOps()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
-            .Returns([@"Q:\results\test.trx"]);
+            .Returns([InResults("test.trx")]);
 
         Assert.IsFalse(await uploader.IsEnabledAsync().ConfigureAwait(false));
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
-        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(InResults("dump.dmp")), CancellationToken.None).ConfigureAwait(false);
         await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
@@ -75,8 +75,8 @@ public async Task TagsOnlyMode_EmitsOnlyBuildTags()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
-        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\crashdump.dmp"), CancellationToken.None).ConfigureAwait(false);
-        await uploader.ConsumeAsync(CreateProducer("HangDumpProcessLifetimeHandler", "Hang dump"), CreateFileArtifact(@"Q:\results\hang\hangdump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(InResults("crashdump.dmp")), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("HangDumpProcessLifetimeHandler", "Hang dump"), CreateFileArtifact(InResults("hang", "hangdump.dmp")), CancellationToken.None).ConfigureAwait(false);
         await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
@@ -104,7 +104,7 @@ public async Task FilesMode_EmitsOnlyArtifactLines()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
-            .Returns([@"Q:\results\a.trx", @"Q:\results\b.dmp"]);
+            .Returns([InResults("a.trx"), InResults("b.dmp")]);
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
         await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
@@ -128,13 +128,13 @@ public async Task FilesMode_SkipsArtifactsOutsideResultsDirectory()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
-            .Returns([@"Q:\results\inside.trx", @"Q:\results-other\outside.trx"]);
+            .Returns([InResults("inside.trx"), OutsideResults("outside.trx")]);
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
         CollectionAssert.AreEqual(
-            new[] { @"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]Q:\results\inside.trx" },
+            new[] { $"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]{InResults("inside.trx")}" },
             GetFormattedLines());
     }
 
@@ -150,10 +150,10 @@ public async Task AllMode_EmitsBuildTagsAndArtifactLines()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
-            .Returns([@"Q:\results\test.trx"]);
+            .Returns([InResults("test.trx")]);
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
-        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(InResults("dump.dmp")), CancellationToken.None).ConfigureAwait(false);
         await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
@@ -162,7 +162,7 @@ public async Task AllMode_EmitsBuildTagsAndArtifactLines()
             {
                 "##vso[build.addbuildtag]has-crashdump",
                 "##vso[build.addbuildtag]has-test-failures",
-                @"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]Q:\results\test.trx",
+                $"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]{InResults("test.trx")}",
             },
             GetFormattedLines());
     }
@@ -172,8 +172,8 @@ public async Task IncludeAndExcludeGlobs_AreAppliedToArtifactUploads()
     {
         AzureDevOpsArtifactUploader uploader = CreateUploader(new Dictionary<string, string[]>
         {
-            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude] = ["skip\\**"],
-            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude] = ["**\\*.trx"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactExclude] = ["skip/**"],
+            [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactInclude] = ["**/*.trx"],
             [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactName] = ["Artifacts"],
             [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles],
         });
@@ -181,13 +181,13 @@ public async Task IncludeAndExcludeGlobs_AreAppliedToArtifactUploads()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
-            .Returns([@"Q:\results\keep.trx", @"Q:\results\keep.coverage", @"Q:\results\skip\ignored.trx"]);
+            .Returns([InResults("keep.trx"), InResults("keep.coverage"), InResults("skip", "ignored.trx")]);
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
         CollectionAssert.AreEqual(
-            new[] { @"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]Q:\results\keep.trx" },
+            new[] { $"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]{InResults("keep.trx")}" },
             GetFormattedLines());
     }
 
@@ -202,8 +202,8 @@ public async Task ConsumedArtifacts_DetectCrashAndHangDumps()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
-        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
-        await uploader.ConsumeAsync(CreateProducer("HangDumpProcessLifetimeHandler", "Hang dump"), CreateFileArtifact(@"Q:\results\hang\dump.log"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(InResults("dump.dmp")), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("HangDumpProcessLifetimeHandler", "Hang dump"), CreateFileArtifact(InResults("hang", "dump.log")), CancellationToken.None).ConfigureAwait(false);
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
         CollectionAssert.AreEquivalent(
@@ -226,7 +226,7 @@ public async Task NonDumpProducer_DoesNotEmitDumpTags()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
-        await uploader.ConsumeAsync(CreateProducer("GenericProducer", "Generic"), CreateFileArtifact(@"Q:\results\hang\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("GenericProducer", "Generic"), CreateFileArtifact(InResults("hang", "dump.dmp")), CancellationToken.None).ConfigureAwait(false);
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
         Assert.IsEmpty(GetFormattedLines());
@@ -260,10 +260,10 @@ public async Task MissingTfBuild_EmitsWarningAndSkipsOutput()
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns((string?)null);
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories))
-            .Returns([@"Q:\results\test.trx"]);
+            .Returns([InResults("test.trx")]);
 
         await uploader.OnTestSessionStartingAsync(new TestSessionContext()).ConfigureAwait(false);
-        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(@"Q:\results\dump.dmp"), CancellationToken.None).ConfigureAwait(false);
+        await uploader.ConsumeAsync(CreateProducer("CrashDumpProcessLifetimeHandler", "Crash dump"), CreateFileArtifact(InResults("dump.dmp")), CancellationToken.None).ConfigureAwait(false);
         await uploader.ConsumeAsync(CreateProducer(), CreateFailedTestNodeUpdateMessage(), CancellationToken.None).ConfigureAwait(false);
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
@@ -319,7 +319,8 @@ public async Task ArtifactUploadPaths_AreEscaped()
             [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifacts] = [AzureDevOpsCommandLineOptions.AzureDevOpsUploadArtifactsModeFiles],
         });
 
-        string specialPath = "Q:\\results\\semi;line\r\nname.trx";
+        string specialPath = $"{ResultsDirectory}{Path.DirectorySeparatorChar}semi;line\r\nname.trx";
+        string escapedSpecialPath = specialPath.Replace(";", "%3B", StringComparison.Ordinal).Replace("\r", "%0D", StringComparison.Ordinal).Replace("\n", "%0A", StringComparison.Ordinal);
         _ = _environmentMock.Setup(environment => environment.GetEnvironmentVariable("TF_BUILD")).Returns("true");
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.ExistDirectory(ResultsDirectory)).Returns(true);
         _ = _fileSystemMock.Setup(fileSystem => fileSystem.GetFiles(ResultsDirectory, "*", SearchOption.AllDirectories)).Returns([specialPath]);
@@ -328,7 +329,7 @@ public async Task ArtifactUploadPaths_AreEscaped()
         await uploader.OnTestSessionFinishingAsync(new TestSessionContext()).ConfigureAwait(false);
 
         CollectionAssert.AreEqual(
-            new[] { "##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]Q:\\results\\semi%3Bline%0D%0Aname.trx" },
+            new[] { $"##vso[artifact.upload containerfolder=Artifacts;artifactname=Artifacts]{escapedSpecialPath}" },
             GetFormattedLines());
     }
 
@@ -417,6 +418,12 @@ private static TestNodeUpdateMessage CreateTestNodeUpdateMessage(TestNodeStatePr
     private static IDataProducer CreateProducer(string uid = "Producer", string displayName = "Producer")
         => new TestProducer(uid, displayName);
 
+    private static string InResults(params string[] segments)
+        => segments.Aggregate(ResultsDirectory, Path.Combine);
+
+    private static string OutsideResults(params string[] segments)
+        => segments.Aggregate(Path.Combine(Path.GetTempPath(), "azdo-artifact-uploader-tests-outside"), Path.Combine);
+
     private string[] GetFormattedLines()
         => [.. _outputData.OfType<FormattedTextOutputDeviceData>().Select(output => output.Text)];
 

From bbe254c232f50e5b13de61d54ad68a00acef02db Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 18 May 2026 07:21:15 +0000
Subject: [PATCH 3/4] Refine portable test path constants

Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
---
 .../AzureDevOpsArtifactUploaderTests.cs                        | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs
index 393565aa0a..f937127915 100644
--- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs
+++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsArtifactUploaderTests.cs
@@ -23,6 +23,7 @@ public sealed class AzureDevOpsArtifactUploaderTests
 {
     private const string ArtifactUploadOptionsRequireUploadArtifactsMessage = "'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.";
     private static readonly string ResultsDirectory = Path.Combine(Path.GetTempPath(), "azdo-artifact-uploader-tests");
+    private static readonly string OutsideResultsDirectory = Path.Combine(Path.GetTempPath(), "azdo-artifact-uploader-tests-outside");
 
     private readonly Mock<IConfiguration> _configurationMock = new();
     private readonly Mock<IEnvironment> _environmentMock = new();
@@ -422,7 +423,7 @@ private static string InResults(params string[] segments)
         => segments.Aggregate(ResultsDirectory, Path.Combine);
 
     private static string OutsideResults(params string[] segments)
-        => segments.Aggregate(Path.Combine(Path.GetTempPath(), "azdo-artifact-uploader-tests-outside"), Path.Combine);
+        => segments.Aggregate(OutsideResultsDirectory, Path.Combine);
 
     private string[] GetFormattedLines()
         => [.. _outputData.OfType<FormattedTextOutputDeviceData>().Select(output => output.Text)];

From b5bd2164e6347c2edccba2fdd681af17aff1fa33 Mon Sep 17 00:00:00 2001
From: Evangelink <11340282+Evangelink@users.noreply.github.com>
Date: Tue, 19 May 2026 16:02:54 +0200
Subject: [PATCH 4/4] Fix merge fallout: restore missing using, drop dead code,
 regenerate xlf

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 .../AzureDevOpsExtensions.cs                  |  1 +
 .../AzureDevOpsReporter.cs                    |  5 +--
 .../Resources/xlf/AzureDevOpsResources.cs.xlf | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.de.xlf | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.es.xlf | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.fr.xlf | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.it.xlf | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.ja.xlf | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.ko.xlf | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.pl.xlf | 40 +++++++++++++++++++
 .../xlf/AzureDevOpsResources.pt-BR.xlf        | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.ru.xlf | 40 +++++++++++++++++++
 .../Resources/xlf/AzureDevOpsResources.tr.xlf | 40 +++++++++++++++++++
 .../xlf/AzureDevOpsResources.zh-Hans.xlf      | 40 +++++++++++++++++++
 .../xlf/AzureDevOpsResources.zh-Hant.xlf      | 40 +++++++++++++++++++
 15 files changed, 522 insertions(+), 4 deletions(-)

diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs
index d71c8a6c94..148b0afcb3 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs
@@ -4,6 +4,7 @@
 using Microsoft.Testing.Extensions.AzureDevOpsReport;
 using Microsoft.Testing.Extensions.Reporting;
 using Microsoft.Testing.Platform.Builder;
+using Microsoft.Testing.Platform.Extensions;
 using Microsoft.Testing.Platform.Services;
 
 namespace Microsoft.Testing.Extensions;
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs
index b7eb6c83c2..ddd2e6d540 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs
@@ -397,15 +397,12 @@ private void EnsureEnabledConfigurationLoaded()
         _hasLoadedEnabledConfiguration = true;
     }
 
-    private static string GetTargetFrameworkMoniker()
-        => TargetFrameworkParser.GetShortTargetFramework(Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkDisplayName)
-            ?? TargetFrameworkParser.GetShortTargetFramework(RuntimeInformation.FrameworkDescription);
-
     private static string GetTestName(TestNode testNode)
         => testNode.Properties
             .OfType<SerializableKeyValuePairStringProperty>()
             .FirstOrDefault(static property => property.Key == FullyQualifiedNamePropertyKey)?.Value
             ?? testNode.DisplayName;
+
     /// <summary>
     /// Formats the reporter message so the test name lands on its own line.
     /// PR check UIs (GitHub Checks via the dotnet problem matcher and Azure DevOps)
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf
index 2a2d9ac36f..1c03349ecc 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="cs" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">Závažnost, která se má použít pro hlášenou událost. Možnosti: error (výchozí) a warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf
index 4e019a7afd..a9da8e98ed 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="de" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">Schweregrad, der für das gemeldete Ereignis verwendet werden soll. Optionen sind: error (Standard) und warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf
index 40c2a008d7..05ed7ecefa 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="es" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">Gravedad que se va a usar para el evento notificado. Las opciones son: error (valor predeterminado) y warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf
index 4f82aa0755..2f1b47604c 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="fr" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">Gravité à utiliser pour l’événement signalé. Les options sont : error (par défaut) et warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf
index 79f20267c5..9692ea0a04 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="it" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">Gravità da usare per l'evento segnalato. Le opzioni sono: error (impostazione predefinita) e warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf
index 2aefa6edb2..6dffb513ad 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="ja" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">報告されたイベントに使用する重大度。オプションは、error (既定) と warning です。</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf
index 37ef6d3a84..c7c450887a 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="ko" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">보고된 이벤트에 사용할 심각도입니다. 옵션은 error(기본값)와 warning, 두 가지입니다.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf
index 2492a4dd60..4bc5ec929d 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="pl" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">Ważność do użycia dla zgłoszonego zdarzenia. Dostępne opcje to: error (domyślny) i warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf
index da434eceec..ea099b0771 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="pt-BR" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">A gravidade que será usada para o evento relatado. As opções são: error (padrão) e warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf
index 9ea04a2a73..446cd14c03 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="ru" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">Уровень серьезности, используемый для события из отчета. Параметры: error (по умолчанию) и warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf
index 2857d58967..112c619ea7 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="tr" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">Raporlanan olay için kullanılacak önem derecesi. Seçenekler şunlardır: error (varsayılan) ve warning.</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf
index d5bf9341d5..0664b58d63 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="zh-Hans" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">要用于所报告事件的严重性。选项为: error (默认)和 warning。</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf
index 657303d699..e005701285 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf
@@ -2,6 +2,16 @@
 <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
   <file datatype="xml" source-language="en" target-language="zh-Hant" original="../AzureDevOpsResources.resx">
     <body>
+      <trans-unit id="ArtifactUploadOptionsRequireUploadArtifacts">
+        <source>'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</source>
+        <target state="new">'--report-azdo-upload-artifact-include', '--report-azdo-upload-artifact-exclude', and '--report-azdo-upload-artifact-name' require '--report-azdo-upload-artifacts' to be set to a value other than 'off'.</target>
+        <note>{Locked="--report-azdo-upload-artifact-include"}{Locked="--report-azdo-upload-artifact-exclude"}{Locked="--report-azdo-upload-artifact-name"}{Locked="--report-azdo-upload-artifacts"}{Locked="off"}</note>
+      </trans-unit>
+      <trans-unit id="ArtifactUploadRequiresTfBuildWarning">
+        <source>Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</source>
+        <target state="new">Azure DevOps artifact upload was requested, but TF_BUILD is not set to 'true'; skipping Azure DevOps artifact upload and build tags.</target>
+        <note>{Locked="TF_BUILD"}{Locked="true"}</note>
+      </trans-unit>
       <trans-unit id="AzureDevOpsDemoteKnownFlakyRequiresAzureDevOps">
         <source>'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</source>
         <target state="new">'--report-azdo-demote-known-flaky' requires '--report-azdo' to be enabled</target>
@@ -82,6 +92,16 @@
         <target state="new">Azure DevOps flaky history returned {0} runs. Only the first {1} runs will be inspected.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidArtifactUploadGlob">
+        <source>Invalid glob pattern {0}.</source>
+        <target state="new">Invalid glob pattern {0}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidArtifactUploadMode">
+        <source>Invalid artifact upload mode {0}.</source>
+        <target state="new">Invalid artifact upload mode {0}.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidFlakyHistoryDays">
         <source>Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</source>
         <target state="new">Invalid value '{0}' for '--report-azdo-flaky-history'. Provide an integer between 1 and 90.</target>
@@ -132,6 +152,26 @@
         <target state="translated">要用於所報告事件的嚴重性。選項為: error (預設) warning。</target>
         <note>{Locked="error"}{Locked="warning"}</note>
       </trans-unit>
+      <trans-unit id="UploadArtifactExcludeOptionDescription">
+        <source>Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</source>
+        <target state="new">Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="UploadArtifactIncludeOptionDescription">
+        <source>Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</source>
+        <target state="new">Include files in Azure DevOps artifact upload using glob patterns relative to the test results directory. Defaults to '**/*'.</target>
+        <note>{Locked="**/*"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactNameOptionDescription">
+        <source>Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</source>
+        <target state="new">Override the Azure DevOps artifact container name. Defaults to 'TestResults_{assemblyName}_{tfm}'.</target>
+        <note>{Locked="TestResults_{assemblyName}_{tfm}"}</note>
+      </trans-unit>
+      <trans-unit id="UploadArtifactsOptionDescription">
+        <source>Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</source>
+        <target state="new">Upload test result files and/or add build tags to Azure DevOps. Options are: off (default), tags-only, files, and all.</target>
+        <note>{Locked="off"}{Locked="tags-only"}{Locked="files"}{Locked="all"}</note>
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
