Skip to content

Add dotnetup <> install under admin mode MVP - #56664

Open
nagilson wants to merge 50 commits into
dotnet:release/dnupfrom
nagilson:nagilson-dotnetup-admin-mvp
Open

nagilson wants to merge 50 commits into
dotnet:release/dnupfrom
nagilson:nagilson-dotnetup-admin-mvp

Conversation

@nagilson

@nagilson nagilson commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Implements the MVP of dotnetup <> install --untracked under admin mode, per the specification in #56603. Please read that specification for details / design components, such as how PATH handling is owned by the admin installer (.exe MSI bundle) rather than dotnetup.

In this PR:

  • admin mode is added as a configuration, only for Windows
  • install functionality is added at a relatively straightforward level.
  • We permit blocking non-admin mode scenarios via policy. daily builds under admin mode are disabled by default but enable-able via policy settings in the registry, considering they are unsigned.
  • dotnetup fails on all other commands or arguments under admin mode, to prevent buggy or unexpected behavior.
  • Offline scenarios and others are not yet supported, similar to dotnetup in other contexts.

Timeout handling and process termination / cancellation of the admin installer is left for another PR to reduce scope: nagilson#211

Initial Switch

image

Examples of Unsupported Scenarios

image image

It handles cancellation

image

It Installs non daily builds when online locally

image

If you ctrl + c (we can't stop the admin installer)

image

Going out of Admin mode (UAC again)

image

nagilson and others added 20 commits October 12, 2026 15:07
Adds DotnetAccessMode.Admin, the DotnetupPolicy registry store for the AdminMode and BlockUnsignedDownloads policy values, the access mode resolver, the install mechanism request option, admin mode error codes, and the AdminModeSupport command contract. No behavior is wired yet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds dotnetup.access_mode and dotnetup.access_mode_source to the common telemetry attributes and excludes admin mode from the ADX dashboard.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds AdminModeGate, which rejects commands and explicitly supplied options or arguments that are not allowed in admin mode. Commands are blocked by default.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds init --mode and admin mode switching through a single elevated operation that sets or clears the AdminMode policy value. Enabling admin mode also sets BlockUnsignedDownloads.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In admin mode, sdk and runtime installs download the official .exe bundle, verify its hash and Authenticode signature, and run it into Program Files. Admin installs require --untracked and never touch the dotnetup manifest.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The admin install check loaded the system hostfxr into dotnetup, locking a file the Windows installer may need to replace. Query the install root's dotnet executable in a child process instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Verify the locked installer against the downloader's expected hash, decide the unsigned allowance from the actual download source, and stop launching further installers after Ctrl+C.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed dotnetup installs verify the replacement's Authenticode signature, so BlockUnsignedDownloads no longer blocks their self update. init and env set now leave admin mode only right before applying the new mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Corrects the admin mode design to the implemented policy layout (AdminMode and BlockUnsignedDownloads 0/1 values under the Policies key), init --mode semantics, unsigned daily installer handling, installer verification and exit codes, and documents init --mode and env set admin.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

azure-pipelines Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@nagilson
nagilson requested a balanced review from Copilot October 15, 2026 22:38
@nagilson nagilson changed the title Nagilson dotnetup admin mvp Add dotnetup <> install under admin mode MVP Oct 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Organizational policy handling conflicts with the specification, and long-running installers can cause batch preparation to time out.

6 open findings
What changed in this PR

Adds Windows-only dotnetup admin-mode installation through official installers, alongside the existing archive-based installation workflow.

Changes:

  • Adds machine-wide mode switching and command restrictions.
  • Adds installer selection, verification, execution, and cancellation handling.
  • Extends tests, telemetry, and user documentation.
File Description
test/​dotnetup.Tests/​WindowsBundleArtifactTests.cs Tests bundle verification and execution.
test/​dotnetup.Tests/​TelemetryTests.cs Tests access-mode telemetry.
test/​dotnetup.Tests/​TelemetryDrainE2ETests.cs Checks persisted mode tags.
test/​dotnetup.Tests/​SelfUpdateSignatureTests.cs Tests signed-update policy exemptions.
test/​dotnetup.Tests/​ReleaseManifestFilterTests.cs Tests installer selection.
test/​dotnetup.Tests/​Mocks/​MockArchiveDownloader.cs Supplies verification metadata.
test/​dotnetup.Tests/​InstallWorkflowTests.cs Tests admin install routing.
test/​dotnetup.Tests/​InstallerOrchestratorTests.cs Tests mechanisms and cancellation.
test/​dotnetup.Tests/​InitFormRendererTests.cs Adjusts form-height expectations.
test/​dotnetup.Tests/​InitFormModelTests.cs Tests Admin selection.
test/​dotnetup.Tests/​EnvCommandTests.cs Isolates machine policy.
test/​dotnetup.Tests/​DotnetupPolicyTests.cs Tests policy storage.
test/​dotnetup.Tests/​DotnetupConfigTests.cs Rejects per-user Admin configuration.
test/​dotnetup.Tests/​DotnetDownloaderTests.cs Tests accepted hashes.
test/​dotnetup.Tests/​DotnetDownloaderBlobFeedTests.cs Tests bundle downloads and policy.
test/​dotnetup.Tests/​AdminModeSwitcherTests.cs Tests elevated mode switching.
test/​dotnetup.Tests/​AdminModeSupportTests.cs Tests command allowlists.
test/​dotnetup.Tests/​AdminModeGateTests.cs Tests argument restrictions.
test/​dotnetup.Tests/​AdminModeGateExecutionTests.cs Tests execution gating.
test/​dotnetup.Tests/​AdminModeCommandTests.cs Tests mode-switch command flows.
test/​dotnetup.Tests/​AdminInstallTests.cs Tests roots and installation discovery.
test/​dotnetup.Tests/​AdminAccessModeTests.cs Tests mode constraints and errors.
test/​dotnetup.Tests/​AccessModeResolverTests.cs Tests effective-mode resolution.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​UnsignedSourcePolicy.cs Delegates registry policy reads.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​ReleaseManifest.cs Selects Windows bundles.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​InstallBatchContext.cs Tracks batch cancellation.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​IInstallArtifact.cs Abstracts installation artifacts.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​IArchiveDownloader.cs Returns verification metadata.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​DotnetupPolicy.cs Implements machine policy storage.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​DotnetInstall.cs Adds installation mechanism selection.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​DotnetDownloader.cs Supports verified bundle downloads.
src/​Installer/​Microsoft.Dotnet.Installation/​Internal/​DotnetArchiveExtractor.cs Implements the artifact interface.
src/​Installer/​Microsoft.Dotnet.Installation/​DotnetInstallException.cs Adds admin installation errors.
src/​Installer/​dotnetup.Library/​WindowsPathHelper.cs Supports directory-free elevated operations.
src/​Installer/​dotnetup.Library/​WindowsInstaller/​WindowsBundleArtifact.cs Verifies and installs bundles.
src/​Installer/​dotnetup.Library/​WindowsInstaller/​MuxerInstallQuery.cs Queries installations through child processes.
src/​Installer/​dotnetup.Library/​WindowsInstaller/​InstallerSignatureVerifier.cs Checks installer signatures.
src/​Installer/​dotnetup.Library/​WindowsInstaller/​BundleProcessRunner.cs Runs bundles and handles cancellation.
src/​Installer/​dotnetup.Library/​WindowsInstaller/​BundleExitCodes.cs Classifies installer results.
src/​Installer/​dotnetup.Library/​Telemetry/​TelemetryTagNames.cs Defines mode and mechanism tags.
src/​Installer/​dotnetup.Library/​Telemetry/​TelemetryCommonProperties.cs Emits access-mode attributes.
src/​Installer/​dotnetup.Library/​Telemetry/​ErrorCategoryClassifier.cs Categorizes new errors.
src/​Installer/​dotnetup.Library/​Telemetry/​dotnetup-adx-dashboard.json Excludes admin-mode events.
src/​Installer/​dotnetup.Library/​SelfUpdate/​SelfUpdateSignature.cs Exposes signed-replacement requirements.
src/​Installer/​dotnetup.Library/​SelfUpdate/​SelfUpdateNotifier.cs Applies signed-update policy exemptions.
src/​Installer/​dotnetup.Library/​InstallerOrchestratorSingleton.cs Coordinates bundle installation and validation.
src/​Installer/​dotnetup.Library/​IInstallationValidator.cs Adds validation failure details.
src/​Installer/​dotnetup.Library/​HostFxrInstallationValidator.cs Validates installer-managed installations.
src/​Installer/​dotnetup.Library/​EnvSettingsApplier.cs Rejects direct Admin application.
src/​Installer/​dotnetup.Library/​DotnetupConfig.cs Keeps Admin outside user configuration.
src/​Installer/​dotnetup.Library/​DotnetAccessModePolicy.cs Restricts Admin to Windows.
src/​Installer/​dotnetup.Library/​docs/​dotnetup-telemetry.md Documents access-mode telemetry.
src/​Installer/​dotnetup.Library/​Commands/​Shared/​InstallWorkflow.cs Routes admin requests to system roots.
src/​Installer/​dotnetup.Library/​Commands/​Shared/​InstallCommand.cs Declares supported install arguments.
src/​Installer/​dotnetup.Library/​Commands/​Self/​SelfUpdateCommand.cs Enables policy-aware admin self-update.
src/​Installer/​dotnetup.Library/​Commands/​Init/​InitWorkflows.cs Coordinates initialization and mode switching.
src/​Installer/​dotnetup.Library/​Commands/​Init/​InitDefaultsResolver.cs Prevents default Admin selection.
src/​Installer/​dotnetup.Library/​Commands/​Init/​InitCommandParser.cs Adds --mode.
src/​Installer/​dotnetup.Library/​Commands/​Init/​InitCommand.cs Routes explicit mode requests.
src/​Installer/​dotnetup.Library/​Commands/​Init/​Form/​InitFormModel.cs Adds Admin choice and explanations.
src/​Installer/​dotnetup.Library/​Commands/​Env/​EnvSetCommandParser.cs Reuses shared mode parsing.
src/​Installer/​dotnetup.Library/​Commands/​Env/​EnvSetCommand.cs Handles machine-mode transitions.
src/​Installer/​dotnetup.Library/​Commands/​ElevatedSystemPath/​ElevatedSystemPathCommandParser.cs Parses elevated policy operations.
src/​Installer/​dotnetup.Library/​Commands/​ElevatedSystemPath/​ElevatedSystemPathCommand.cs Executes elevated policy changes.
src/​Installer/​dotnetup.Library/​CommandBase.cs Enforces admin restrictions.
src/​Installer/​dotnetup.Library/​AdminModeSwitcher.cs Coordinates elevated mode changes.
src/​Installer/​dotnetup.Library/​AdminModeSupport.cs Defines command support contracts.
src/​Installer/​dotnetup.Library/​AdminModeGate.cs Rejects unsupported commands and arguments.
src/​Installer/​dotnetup.Library/​AdminInstallRootResolver.cs Resolves architecture-specific system roots.
src/​Installer/​dotnetup.Library/​AccessModeResolver.cs Resolves policy and user preferences.
src/​Installer/​dotnetup.Library/​AccessModeCliParsing.cs Centralizes mode parsing.
documentation/​general/​dotnetup/​signature-verification.md Documents installer trust and policy.
documentation/​general/​dotnetup/​reference/​dotnetup-init.md Documents initialization mode selection.
documentation/​general/​dotnetup/​reference/​dotnetup-env-set.md Documents mode transitions.
documentation/​general/​dotnetup/​concepts/​environment.md Explains Admin environment behavior.
.github/​memory/​FILE_MAP.md Maps admin-mode implementation.

🧠 Review effort: Balanced

Comment thread src/Installer/dotnetup.Library/Commands/Init/Form/InitFormModel.cs
Comment thread src/Installer/dotnetup.Library/Commands/Init/InitCommandParser.cs
Comment thread src/Installer/dotnetup.Library/InstallerOrchestratorSingleton.cs Outdated
Comment thread src/Installer/dotnetup.Library/InstallerOrchestratorSingleton.cs
Comment thread src/Installer/dotnetup.Library/WindowsInstaller/WindowsBundleArtifact.cs Outdated

@nagilson nagilson left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env script gets written to profiles and fails under admin mode - it should also not resurface user level .net installs with dotnetup if the tags persist - they need to be removedbut dotnetup should remain on the path if switching to admin mode.

We are also missing a hostfxr probe instead of the manifest check for already installed scenarios but I'm ok to defer that to another issue.

I need to continue this later.

Comment thread src/Installer/dotnetup.Library/Commands/Init/Form/InitFormModel.cs Outdated
Comment thread src/Installer/dotnetup.Library/AccessModeResolver.cs Outdated
Comment thread src/Installer/Microsoft.Dotnet.Installation/Internal/DotnetupPolicy.cs Outdated
Comment thread src/Installer/dotnetup.Library/DotnetupConfig.cs
Comment thread src/Installer/Microsoft.Dotnet.Installation/Internal/DotnetupPolicy.cs Outdated
Comment thread src/Installer/dotnetup.Library/WindowsInstaller/BundleProcessRunner.cs Outdated
Comment thread src/Installer/dotnetup.Library/WindowsInstaller/InstallerSignatureVerifier.cs Outdated
nagilson and others added 4 commits October 16, 2026 13:18
Address review feedback on localized messages, concise command names, and unsupported-symbol selection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Preserve the prior migration selection when switching back to an archive mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the admin-mode requirement in validation instead of forcing the request contract from the current mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nagilson and others added 7 commits October 19, 2026 13:22
Make DotnetupPolicy an instance service over an IDotnetupPolicyStore, with
the machine store (WindowsRegistryPolicyStore on Windows, UnixPolicyStore
for the sentinel file elsewhere) as the production default. AccessModeResolver,
AdminModeSwitcher, CommandBase (and the commands that need it), DotnetDownloader,
WindowsBundleArtifact, SelfUpdateNotifier and telemetry common properties
take the policy through constructor or method parameters.

Remove DotnetupPolicy.Store/ResetStoreForTesting,
AccessModeResolver.OverrideForTesting and UnsignedSourcePolicy.OverrideForTesting.
Tests now construct the real services over a FakeDotnetupPolicyStore, and the
fake elevator applies changes with the same code as the elevated child.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep --untracked validation at the command workflow boundary that owns the option.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the stage-0-compatible baseline TFM for standalone fixtures and reuse the pipeline-built managed dotnetup assembly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move bundle download, verification, process, restart, and exit-code messages into resources and regenerate XLF files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…out holding onto the muxer

doing it in process will cause us to block other dotnetup instances or from us replacing the hostfxr.dll ourselves - doing it out of proc will help with that problem.
@nagilson
nagilson marked this pull request as ready for review October 19, 2026 21:36
@nagilson
nagilson requested a balanced review from Copilot October 19, 2026 21:36
Copilot stopped reviewing on behalf of nagilson due to an error October 19, 2026 21:57
Copilot stopped reviewing on behalf of nagilson due to an error October 19, 2026 22:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nagilson
nagilson requested review from a team, dsplaisted and joeloff October 19, 2026 23:01
Disable Spectre default profile enrichment for explicitly configured test consoles and cover local and Azure Pipelines environments at multiple terminal widths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
/// signature on the replacement for <paramref name="installedPath"/>. Such a download is not
/// trusted on its hash alone, so the unsigned-download policy does not apply to it.
/// </summary>
[SupportedOSPlatformGuard("windows5.1.2600")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you checking against XP? .NET has a list of compatible/supported OS versions for each .NET release.

/// and every other user, including the same user running without elevation, can only read.
/// </summary>
[SupportedOSPlatform("windows")]
internal static class SecureScratchDirectory

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nagilson We should talk - this isn't necessary.

}
}

private static string RequireFolder(string folder)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of this method?

private const int LOAD_WITH_ALTERED_SEARCH_PATH = 0x8;

[LibraryImport("kernel32.dll", StringMarshalling = StringMarshalling.Utf16)]
[LibraryImport("kernel32.dll", StringMarshalling = StringMarshalling.Utf16, SetLastError = true)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this loaded from a safe location?


`/uninstall` respects reference counts but only VS and a few others respect it - e.g. when tools like PowerToys give the user a link to download the Runtime, that is not reference counted, so garbage collection as it is for non-`admin` modes would remove what users had already requested if the installs are not respected from other sources.

We also know `/uninstall` on the `.exe` may return `0` even if the install was not uninstalled if other references remain so we must make our own hostfxr API check afterward to see if something was actually uninstalled or not.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hostfxr has no knowledge about actual installs. It can tell you what runtimes it thinks are on disk, but nothing about actual installs.


`dotnetup uninstall` will not initially be supported in `admin` mode.

`/uninstall` respects reference counts but only VS and a few others respect it - e.g. when tools like PowerToys give the user a link to download the Runtime, that is not reference counted, so garbage collection as it is for non-`admin` modes would remove what users had already requested if the installs are not respected from other sources.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it? I installed PowerToys, they ship as a self-contained application, but yes, there are plenty of applications that either prompt or chain various .NET runtimes. I'm not sure what you mean by reference count in this context. The MSI are always ref counted against the parent bundle, but we never ref count the bundles


## Concurrency

Simultaneous commit operations - which in non-admin means 'extract', is not possible with MSI installs. `dotnetup` already defers the extract operation to one sequential step with concurrent downloads. The commit operation will happen sequentially in the same way. Concurrency will be deferred to avoid multi-UAC prompting.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only reason to install sequentially is because per-machine MSIs are not permitted to run concurrently by the window installer service.

| Result | Handling |
| --- | --- |
| `0` | Success. |
| `3010`, `1641` | Success. A restart is required; `dotnetup` reports this and does not restart. |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1641 is considered a success code, but if you ever receive it, it means that a restart is already underway whereas 3010 is to inform the user they may need to restart to complete the install, e.g. pending file renames.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants