Repository: DataDog/datadog-agent
Pull request #35017: (fleet) install dotnet OCI from MSI custom action
Author: login: bmermet
Labels: name: changelog/no-changelog
description: No changelog entry needed
name: [deprecated] team/windows-agent
name: qa/done
description: QA done before merge and regressions are covered by tests
name: long review
description: PR is complex, plan time to review it
Created: 2025-03-11T16:23:30Z | Merged: 2025-03-19T23:40:14Z

<!--
* Contributors are encouraged to read our [CONTRIBUTING](/CONTRIBUTING.md) documentation.
* Both Contributor and Reviewer Checklists are available at https://datadoghq.dev/datadog-agent/guidelines/contributing/#pull-requests.
* The pull request:
  * Should only fix one issue or add one feature at a time.
  * Must update the test suite for the relevant functionality.
  * Should pass all status checks before being reviewed or merged.
* Commit titles should be prefixed with general area of pull request's change.
* Please fill the below sections if possible with relevant information or links.
-->
### What does this PR do?

Add a custom action to the agent MSI to install the dotnet OCI if needed.

### Motivation

Provided single step instrumentation for windows.

### Describe how you validated your changes
<!--
Validate your changes before merge, ensuring that:
* Your PR is tested by static / unit / integrations / e2e tests
* Your PR description details which e2e tests cover your changes, if any
* The PR description contains details of how you validated your changes. If you validated changes manually and not through automated tests, add context on why automated tests did not fit your changes validation.

If you want additional validation by a second person, you can ask reviewers to do it. Describe how to set up an environment for manual tests in the PR description. Manual validation is expected to happen on every commit before merge.

Any manual validation step should then map to an automated test. Manual validation should not substitute automation, minus exceptions not supported by test tooling yet.
-->

Covered by E2E tests.

### Possible Drawbacks / Trade-offs

### Additional Notes
<!--
* Anything else we should know when reviewing?
* Include benchmarking information here whenever possible.
* Include info about alternatives that were considered and why the proposed
  version was chosen.
-->


Diff:
diff --git a/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/base_suite.go b/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/base_suite.go
new file mode 100644
index 000000000000..eb9512e98afa
--- /dev/null
+++ b/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/base_suite.go
@@ -0,0 +1,89 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016-present Datadog, Inc.
+
+// Package dotnettests contains the E2E tests for the .NET APM Library package.
+package dotnettests
+
+import (
+	"strings"
+
+	installerwindows "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows"
+	"github.com/DataDog/datadog-agent/test/new-e2e/tests/windows"
+)
+
+type baseIISSuite struct {
+	installerwindows.BaseSuite
+}
+
+func (s *baseIISSuite) SetupSuite() {
+	s.BaseSuite.SetupSuite()
+	s.installIIS()
+	s.installAspNet()
+}
+
+func (s *baseIISSuite) installIIS() {
+	host := s.Env().RemoteHost
+	err := windows.InstallIIS(host)
+	s.Require().NoError(err)
+}
+
+func (s *baseIISSuite) installAspNet() {
+	host := s.Env().RemoteHost
+	output, err := host.Execute("Install-WindowsFeature Web-Asp-Net45")
+	s.Require().NoErrorf(err, "failed to install Asp.Net: %s", output)
+}
+
+func (s *baseIISSuite) startIISApp(webConfigFile, aspxFile []byte) {
+	host := s.Env().RemoteHost
+	err := host.MkdirAll("C:\\inetpub\\wwwroot\\DummyApp")
+	s.Require().NoError(err, "failed to create directory for DummyApp")
+	_, err = host.WriteFile("C:\\inetpub\\wwwroot\\DummyApp\\web.config", webConfigFile)
+	s.Require().NoError(err, "failed to write web.config file")
+	_, err = host.WriteFile("C:\\inetpub\\wwwroot\\DummyApp\\index.aspx", aspxFile)
+	s.Require().NoError(err, "failed to write index.aspx file")
+	script := `
+$SitePath = "C:\inetpub\wwwroot\DummyApp"
+New-WebSite -Name DummyApp -PhysicalPath $SitePath -Port 8080 -ApplicationPool "DefaultAppPool" -Force
+Stop-WebSite -Name "DummyApp"
+Start-WebSite -Name "DummyApp"
+$state = (Get-WebAppPoolState -Name "DefaultAppPool").Value
+if ($state -eq "Stopped") {
+    Start-WebAppPool -Name "DefaultAppPool"
+}
+Restart-WebAppPool -Name "DefaultAppPool"
+Invoke-WebRequest -Uri "http://localhost:8080/index.aspx" -UseBasicParsing
+	`
+	output, err := host.Execute(script)
+	s.Require().NoErrorf(err, "failed to start site: %s", output)
+}
+
+func (s *baseIISSuite) stopIISApp() {
+	script := `
+Stop-WebSite -Name "DummyApp"
+$state = (Get-WebAppPoolState -Name "DefaultAppPool").Value
+if ($state -ne "Stopped") {
+	Stop-WebAppPool -Name "DefaultAppPool"
+	$retryCount = 0
+	do {
+		Start-Sleep -Seconds 1
+		$status = (Get-WebAppPoolState -Name DefaultAppPool).Value
+		$retryCount++
+	} while ($status -ne "Stopped" -and $retryCount -lt 60)
+	if ($status -ne "Stopped") {
+		exit -1
+	}
+}
+	`
+	host := s.Env().RemoteHost
+	output, err := host.Execute(script)
+	s.Require().NoErrorf(err, "failed to stop site: %s", output)
+}
+
+func (s *baseIISSuite) getLibraryPathFromInstrumentedIIS() string {
+	host := s.Env().RemoteHost
+	output, err := host.Execute(`(Invoke-WebRequest -Uri "http://localhost:8080/index.aspx" -UseBasicParsing).Content`)
+	s.Require().NoErrorf(err, "failed to get content from site: %s", output)
+	return strings.TrimSpace(output)
+}
diff --git a/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/install_test.go b/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/install_test.go
index 693ba9f13a1c..64095747a562 100644
--- a/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/install_test.go
+++ b/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/install_test.go
@@ -15,7 +15,6 @@ import (
 	installer "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/unix"
 	installerwindows "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows"
 	"github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows/consts"
-	"github.com/DataDog/datadog-agent/test/new-e2e/tests/windows"
 
 	"testing"
 )
@@ -28,7 +27,7 @@ var (
 )
 
 type testDotnetLibraryInstallSuite struct {
-	installerwindows.BaseSuite
+	baseIISSuite
 }
 
 // TestDotnetInstalls tests the usage of the Datadog installer to install the apm-library-dotnet-package package.
@@ -38,20 +37,14 @@ func TestDotnetLibraryInstalls(t *testing.T) {
 			winawshost.ProvisionerNoAgentNoFakeIntake()))
 }
 
-func (s *testDotnetLibraryInstallSuite) SetupSuite() {
-	s.BaseSuite.SetupSuite()
-	s.installIIS()
-	s.installAspNet()
-}
-
 func (s *testDotnetLibraryInstallSuite) BeforeTest(suiteName, testName string) {
-	s.BaseSuite.BeforeTest(suiteName, testName)
+	s.baseIISSuite.BeforeTest(suiteName, testName)
 	s.Require().NoError(s.Installer().Install(installerwindows.WithMSILogFile(testName + "-msiinstall.log")))
 }
 
 func (s *testDotnetLibraryInstallSuite) AfterTest(suiteName, testName string) {
 	s.Installer().Purge()
-	s.BaseSuite.AfterTest(suiteName, testName)
+	s.baseIISSuite.AfterTest(suiteName, testName)
 }
 
 // TestInstallUninstallDotnetLibraryPackage tests installing and uninstalling the Datadog APM Library for .NET using the Datadog installer.
@@ -83,7 +76,7 @@ func (s *testDotnetLibraryInstallSuite) TestUpdate() {
 
 	// Start the IIS app to load the library
 	defer s.stopIISApp()
-	s.startIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
 
 	// Check that the expected version of the library is loadedi
 	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
@@ -102,7 +95,7 @@ func (s *testDotnetLibraryInstallSuite) TestUpdate() {
 	s.Require().Host(s.Env().RemoteHost).DirExists(oldLibraryPath, "the old library path: %s should still exist after garbage collection", oldLibraryPath)
 
 	// Restart the IIS application
-	s.startIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
 
 	// Check that the new version of the library is loaded
 	output = s.getLibraryPathFromInstrumentedIIS()
@@ -119,7 +112,7 @@ func (s *testDotnetLibraryInstallSuite) TestRemovePackageFailsIfInUse() {
 	s.installDotnetAPMLibrary()
 
 	defer s.stopIISApp()
-	s.startIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
 
 	output, err := s.Installer().RemovePackage("datadog-apm-library-dotnet")
 	s.Require().Error(err, "Removing the package while the native profiler is used by another process should fail: %s", output)
@@ -169,71 +162,6 @@ func (s *testDotnetLibraryInstallSuite) removeDotnetAPMLibrary() {
 	s.Require().NoErrorf(err, "failed to remove the dotnet library package: %s", output)
 }
 
-func (s *testDotnetLibraryInstallSuite) installIIS() {
-	host := s.Env().RemoteHost
-	err := windows.InstallIIS(host)
-	s.Require().NoError(err)
-}
-
-func (s *testDotnetLibraryInstallSuite) installAspNet() {
-	host := s.Env().RemoteHost
-	output, err := host.Execute("Install-WindowsFeature Web-Asp-Net45")
-	s.Require().NoErrorf(err, "failed to install Asp.Net: %s", output)
-}
-
-func (s *testDotnetLibraryInstallSuite) startIISApp() {
-	host := s.Env().RemoteHost
-	err := host.MkdirAll("C:\\inetpub\\wwwroot\\DummyApp")
-	s.Require().NoError(err, "failed to create directory for DummyApp")
-	_, err = host.WriteFile("C:\\inetpub\\wwwroot\\DummyApp\\web.config", webConfigFile)
-	s.Require().NoError(err, "failed to write web.config file")
-	_, err = host.WriteFile("C:\\inetpub\\wwwroot\\DummyApp\\index.aspx", aspxFile)
-	s.Require().NoError(err, "failed to write index.aspx file")
-	script := `
-$SitePath = "C:\inetpub\wwwroot\DummyApp"
-New-WebSite -Name DummyApp -PhysicalPath $SitePath -Port 8080 -ApplicationPool "DefaultAppPool" -Force
-Stop-WebSite -Name "DummyApp"
-Start-WebSite -Name "DummyApp"
-$state = (Get-WebAppPoolState -Name "DefaultAppPool").Value
-if ($state -eq "Stopped") {
-    Start-WebAppPool -Name "DefaultAppPool"
-}
-Restart-WebAppPool -Name "DefaultAppPool"
-Invoke-WebRequest -Uri "http://localhost:8080/index.aspx" -UseBasicParsing
-	`
-	output, err := host.Execute(script)
-	s.Require().NoErrorf(err, "failed to start site: %s", output)
-}
-
-func (s *testDotnetLibraryInstallSuite) stopIISApp() {
-	script := `
-Stop-WebSite -Name "DummyApp"
-$state = (Get-WebAppPoolState -Name "DefaultAppPool").Value
-if ($state -ne "Stopped") {
-	Stop-WebAppPool -Name "DefaultAppPool"
-	$retryCount = 0
-	do {
-		Start-Sleep -Seconds 1
-		$status = (Get-WebAppPoolState -Name DefaultAppPool).Value
-		$retryCount++
-	} while ($status -ne "Stopped" -and $retryCount -lt 60)
-	if ($status -ne "Stopped") {
-		exit -1
-	}
-}
-	`
-	host := s.Env().RemoteHost
-	output, err := host.Execute(script)
-	s.Require().NoErrorf(err, "failed to stop site: %s", output)
-}
-
-func (s *testDotnetLibraryInstallSuite) getLibraryPathFromInstrumentedIIS() string {
-	host := s.Env().RemoteHost
-	output, err := host.Execute("(Invoke-WebRequest -Uri \"http://localhost:8080/index.aspx\" -UseBasicParsing).Content")
-	s.Require().NoErrorf(err, "failed to get content from site: %s", output)
-	return strings.TrimSpace(output)
-}
-
 // pathJoin and pathDir are helper functions to work with paths in Windows.
 // since the test code is running on a Linux machine filepath functions
 // will not work as expected
diff --git a/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/msi_install_test.go b/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/msi_install_test.go
new file mode 100644
index 000000000000..87d0495dc8a0
--- /dev/null
+++ b/test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/msi_install_test.go
@@ -0,0 +1,363 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016-present Datadog, Inc.
+
+package dotnettests
+
+import (
+	_ "embed"
+	"fmt"
+	"os"
+
+	"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e"
+	winawshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/provisioners/aws/host/windows"
+	installerwindows "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows"
+	"github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows/consts"
+
+	"testing"
+)
+
+type testAgentMSIInstallsDotnetLibrary struct {
+	baseIISSuite
+	previousDotnetLibraryVersion installerwindows.PackageVersion
+	currentDotnetLibraryVersion  installerwindows.PackageVersion
+}
+
+// TestDotnetInstalls tests the usage of the Datadog installer and the MSI to install the apm-library-dotnet-package package.
+func TestAgentMSIInstallsDotnetLibrary(t *testing.T) {
+	e2e.Run(t, &testAgentMSIInstallsDotnetLibrary{},
+		e2e.WithProvisioner(
+			winawshost.ProvisionerNoAgentNoFakeIntake()))
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) SetupSuite() {
+	s.baseIISSuite.SetupSuite()
+
+	s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("PREVIOUS_DOTNET_VERSION_PACKAGE"))
+	if s.previousDotnetLibraryVersion.PackageVersion() == "" {
+		s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58926677.beta.sha-af5a1fab-1")
+	}
+	s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("CURRENT_DOTNET_VERSION_PACKAGE"))
+	if s.currentDotnetLibraryVersion.PackageVersion() == "" {
+		s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58951229.beta.sha-af5a1fab-1")
+	}
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) AfterTest(suiteName, testName string) {
+	s.Installer().Purge()
+	s.baseIISSuite.AfterTest(suiteName, testName)
+}
+
+// TestInstallFromMSI tests the Agent MSI can install the dotnet library OCI package
+func (s *testAgentMSIInstallsDotnetLibrary) TestInstallFromMSI() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Assert
+	s.assertSuccessfulPromoteExperiment(version.Version())
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+// TestMSIThenRemoteUpgrade tests the dotnet library can be remotely upgraded from an Agent MSI installed version
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIThenRemoteUpgrade() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Start remote upgrade experiment
+	_, err := s.startExperimentCurrentVersion()
+	s.Require().NoError(err)
+	s.assertSuccessfulStartExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+
+	// Promote the experiment
+	_, err = s.Installer().PromoteExperiment("datadog-apm-library-dotnet")
+	s.Require().NoError(err)
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+}
+
+// TestUpgradeWithMSI tests the dotnet library can be upgraded from the MSI
+func (s *testAgentMSIInstallsDotnetLibrary) TestUpgradeWithMSI() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Install the second version using a new Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("upgrade.log"),
+	)
+
+	// Check that the new version became the stable version
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+}
+
+// TestMSIRollbackRemovesLibrary tests that the dotnet library is removed when the MSI installation fails
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIRollbackRemovesLibrary() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Assert
+	s.Require().Host(s.Env().RemoteHost).
+		NoDirExists(`C:/ProgramData/Datadog/Installer/packages/datadog-apm-library-dotnet`)
+}
+
+// TestMSISkipRollbackIfInstalled tests that the newly installed dotnet library is not removed when the MSI upgrade fails
+// if another version of the library was already installed.
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSISkipRollbackIfInstalled() {
+	// Arrange
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Rollback during upgrade to the new version
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Start IIS and check that the NEW version of the library is loaded
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+}
+
+// TestUninstallKeepsLibrary tests that the dotnet library is not removed when the Agent MSI is uninstalled
+func (s *testAgentMSIInstallsDotnetLibrary) TestUninstallKeepsLibrary() {
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Install the dotnet library with the Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Uninstall the Agent
+	err := s.Installer().Uninstall(
+		installerwindows.WithMSILogFile("uninstall.log"),
+	)
+	s.Require().NoError(err)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) setAgentConfig() {
+	s.Env().RemoteHost.MkdirAll("C:\\ProgramData\\Datadog")
+	s.Env().RemoteHost.WriteFile(consts.ConfigPath, []byte(`
+api_key: aaaaaaaaa
+remote_updates: true
+`))
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) assertSuccessfulStartExperiment(version string) {
+	s.Require().Host(s.Env().RemoteHost).HasDatadogInstaller().Status().
+		HasPackage("datadog-apm-library-dotnet").
+		WithExperimentVersionMatchPredicate(func(actual string) {
+			s.Require().Contains(actual, version)
+		})
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) assertSuccessfulPromoteExperiment(version string) {
+	s.Require().Host(s.Env().RemoteHost).HasDatadogInstaller().Status().
+		HasPackage("datadog-apm-library-dotnet").
+		WithStableVersionMatchPredicate(func(actual string) {
+			s.Require().Contains(actual, version)
+		}).
+		WithExperimentVersionEqual("")
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) startExperimentCurrentVersion() (string, error) {
+	return s.startExperimentWithCustomPackage(installerwindows.WithName("datadog-apm-library-dotnet"),
+		installerwindows.WithAlias("apm-library-dotnet-package"),
+		// TODO remove override once image is published in prod
+		installerwindows.WithVersion(s.currentDotnetLibraryVersion.PackageVersion()),
+		installerwindows.WithRegistry("install.datad0g.com"),
+		installerwindows.WithDevEnvOverrides("CURRENT_DOTNET_LIBRARY"),
+	)
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) startExperimentWithCustomPackage(opts ...installerwindows.PackageOption) (string, error) {
+	packageConfig, err := installerwindows.NewPackageConfig(opts...)
+	s.Require().NoError(err)
+	packageConfig, err = installerwindows.CreatePackageSourceIfLocal(s.Env().RemoteHost, packageConfig)
+	s.Require().NoError(err)
+
+	// Set catalog so daemon can find the package
+	_, err = s.Installer().SetCatalog(installerwindows.Catalog{
+		Packages: []installerwindows.PackageEntry{
+			{
+				Package: packageConfig.Name,
+				Version: packageConfig.Version,
+				URL:     packageConfig.URL(),
+			},
+		},
+	})
+	s.Require().NoError(err)
+	return s.Installer().StartExperiment("datadog-apm-library-dotnet", packageConfig.Version)
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) installPreviousAgentVersion(opts ...installerwindows.MsiOption) {
+	agentVersion := s.StableAgentVersion().Version()
+	options := []installerwindows.MsiOption{
+		// TODO: switch to prod stable entry when available
+		installerwindows.WithOption(installerwindows.WithURLFromPipeline("59253404")),
+		installerwindows.WithMSILogFile("install-previous-version.log"),
+	}
+	options = append(options, opts...)
+	options = append(options, installerwindows.WithMSIDevEnvOverrides("PREVIOUS_AGENT"))
+	s.Require().NoError(s.Installer().Install(options...))
+
+	// sanity check: make sure we did indeed install the stable version
+	s.Require().Host(s.Env().RemoteHost).
+		HasBinary(consts.BinaryPath).
+		// Don't check the binary signature because it could have been updated since the last stable was built
+		WithVersionMatchPredicate(func(version string) {
+			s.Require().Contains(version, agentVersion)
+		})
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) installCurrentAgentVersion(opts ...installerwindows.MsiOption) {
+	agentVersion := s.CurrentAgentVersion().GetNumberAndPre()
+
+	options := []installerwindows.MsiOption{
+		installerwindows.WithMSILogFile("install-current-version.log"),
+	}
+	options = append(options, opts...)
+	options = append(options, installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"))
+	s.Require().NoError(s.Installer().Install(
+		options...,
+	))
+
+	// sanity check: make sure we did indeed install the stable version
+	s.Require().Host(s.Env().RemoteHost).
+		HasBinary(consts.BinaryPath).
+		// Don't check the binary signature because it could have been updated since the last stable was built
+		WithVersionMatchPredicate(func(version string) {
+			s.Require().Contains(version, agentVersion)
+		})
+}
diff --git a/tools/windows/DatadogAgentInstaller/AgentCustomActions/CustomAction.cs b/tools/windows/DatadogAgentInstaller/AgentCustomActions/CustomAction.cs
index 137fd13e371c..13d837418bc5 100644
--- a/tools/windows/DatadogAgentInstaller/AgentCustomActions/CustomAction.cs
+++ b/tools/windows/DatadogAgentInstaller/AgentCustomActions/CustomAction.cs
@@ -209,5 +209,17 @@ public static ActionResult WriteInstallInfo(Session session)
         {
             return Datadog.CustomActions.InstallInfoCustomActions.WriteInstallInfo(session);
         }
+
+        [CustomAction]
+        public static ActionResult InstallOciPackages(Session session)
+        {
+            return Datadog.CustomActions.InstallOciPackages.InstallPackages(session);
+        }
+
+        [CustomAction]
+        public static ActionResult RollbackOciPackages(Session session)
+        {
+            return Datadog.CustomActions.InstallOciPackages.RollbackActions(session);
+        }
     }
 }
diff --git a/tools/windows/DatadogAgentInstaller/CustomActions/CustomActions.csproj b/tools/windows/DatadogAgentInstaller/CustomActions/CustomActions.csproj
index c6e34e5b04f0..551ab791545f 100644
--- a/tools/windows/DatadogAgentInstaller/CustomActions/CustomActions.csproj
+++ b/tools/windows/DatadogAgentInstaller/CustomActions/CustomActions.csproj
@@ -61,6 +61,7 @@
     <Compile Include="ConfigureUserCustomActions.cs" />
     <Compile Include="Constants.cs" />
     <Compile Include="Flare.cs" />
+    <Compile Include="InstallOciPackages.cs" />
     <Compile Include="Interfaces\IFeatureInfo.cs" />
     <Compile Include="Interfaces\IFileSystemServices.cs" />
     <Compile Include="Interfaces\IInstallerHttpClient.cs" />
@@ -86,6 +87,7 @@
     <Compile Include="Native\ServiceControllerExtensions.cs" />
     <Compile Include="Native\WindowsService.cs" />
     <Compile Include="PrerequisitesCustomActions.cs" />
+    <Compile Include="Rollback\InstallerPackageRollback.cs" />
     <Compile Include="Rollback\ServicePermissionRollbackData.cs" />
     <Compile Include="Rollback\RestoreDaclRollbackCustomAction.cs" />
     <Compile Include="Rollback\FilePermissionRollbackData.cs" />
diff --git a/tools/windows/DatadogAgentInstaller/CustomActions/InstallOciPackages.cs b/tools/windows/DatadogAgentInstaller/CustomActions/InstallOciPackages.cs
new file mode 100644
index 000000000000..9280d743711c
--- /dev/null
+++ b/tools/windows/DatadogAgentInstaller/CustomActions/InstallOciPackages.cs
@@ -0,0 +1,177 @@
+using Datadog.CustomActions.Extensions;
+using Datadog.CustomActions.Interfaces;
+using Datadog.CustomActions.Native;
+using Datadog.CustomActions.Rollback;
+using Microsoft.Deployment.WindowsInstaller;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Datadog.CustomActions
+{
+
+    public class InstallOciPackages
+    {
+        private readonly ISession _session;
+        private readonly string _installerExecutable;
+        private readonly string _site;
+        private readonly string _apiKey;
+        private readonly string _overrideRegistryUrl;
+        private readonly RollbackDataStore _rollbackDataStore;
+
+        public InstallOciPackages(ISession session)
+        {
+            _session = session;
+            var installDir = session.Property("PROJECTLOCATION");
+            _site = session.Property("SITE");
+            _apiKey = session.Property("APIKEY");
+            _overrideRegistryUrl = session.Property("DD_INSTALLER_REGISTRY_URL");
+            _installerExecutable = System.IO.Path.Combine(installDir, "bin", "datadog-installer.exe");
+            _rollbackDataStore = new RollbackDataStore(session, "InstallOciPackages", new FileSystemServices(), new ServiceController());
+        }
+
+
+        private string PackageName(string language)
+        {
+            return $"datadog-apm-library-{language}";
+        }
+
+        private string OciImageName(string language)
+        {
+            return $"apm-library-{language}-package";
+        }
+
+        private string PackageUrl(string language, string version)
+        {
+            if (_site == "datad0g.com")
+            {
+                return $"oci://install.datad0g.com/{OciImageName(language)}:{version}";
+            }
+            else
+            {
+                return $"oci://install.datadoghq.com/{OciImageName(language)}:{version}";
+            }
+        }
+
+        private (string Name, string Version) ParseVersion(string library)
+        {
+            library = library.Trim();
+            var index = library.IndexOf(':');
+            if (index == -1)
+            {
+                return (library, string.Empty);
+            }
+
+            return (library.Substring(0, index), library.Substring(index + 1));
+        }
+
+        private Dictionary<string, string> InstallerEnvironmentVariables()
+        {
+            var env = new Dictionary<string, string>();
+            if (!string.IsNullOrEmpty(_apiKey))
+            {
+                env["DD_API_KEY"] = _apiKey;
+            }
+            if (!string.IsNullOrEmpty(_site))
+            {
+                env["DD_SITE"] = _site;
+            }
+            if (!string.IsNullOrEmpty(_overrideRegistryUrl))
+            {
+                env["DD_INSTALLER_REGISTRY_URL"] = _overrideRegistryUrl;
+            }
+            return env;
+        }
+
+        private ActionResult InstallPackages()
+        {
+            try
+            {
+                _session.Log("Installing Oci Packages");
+                var instrumentationEnabled = _session.Property("DD_APM_INSTRUMENTATION_ENABLED");
+                var librariesRaw = _session.Property("DD_APM_INSTRUMENTATION_LIBRARIES");
+                _session.Log($"instrumentationEnabled: {instrumentationEnabled}");
+                if (string.IsNullOrEmpty(instrumentationEnabled) || string.IsNullOrEmpty(librariesRaw))
+                {
+                    _session.Log($"instrumentation is disabled or no library is provided skipping");
+                    return ActionResult.Success;
+                }
+                if (instrumentationEnabled != "iis")
+                {
+                    _session.Log("Only DD_APM_INSTRUMENTATION_ENABLED=iis is supported");
+                    return ActionResult.Failure;
+                }
+                var libraries = librariesRaw.Split(',');
+                foreach (var library in libraries)
+                {
+                    var libWithVersion = ParseVersion(library);
+                    if (!IsPackageInstalled(libWithVersion.Name))
+                    {
+                        _rollbackDataStore.Add(
+                            new InstallerPackageRollback($"remove {PackageName(libWithVersion.Name)}"));
+                    }
+
+                    InstallPackage(libWithVersion.Name, libWithVersion.Version);
+                }
+
+                return ActionResult.Success;
+            }
+            catch (Exception ex)
+            {
+                _session.Log("Error while installing oci package: " + ex.Message);
+                return ActionResult.Failure;
+            }
+            finally
+            {
+                _rollbackDataStore.Store();
+            }
+        }
+
+        private ActionResult RollbackState()
+        {
+            _rollbackDataStore.Load();
+            _rollbackDataStore.Restore();
+            return ActionResult.Success;
+        }
+
+        private bool IsPackageInstalled(string library)
+        {
+            var packageName = PackageName(library);
+            using (var proc = _session.RunCommand(_installerExecutable, $"is-installed {packageName}", InstallerEnvironmentVariables()))
+            {
+                if (proc.ExitCode == 10)
+                {
+                    return false;
+                }
+
+                if (proc.ExitCode != 0)
+                {
+                    throw new Exception($"'datadog-installer is-installed {packageName}' failed with exit code: {proc.ExitCode}");
+                }
+                return true;
+            }
+        }
+
+        private void InstallPackage(string library, string version)
+        {
+            var ociImageName = OciImageName(library);
+            using (var proc = _session.RunCommand(_installerExecutable, $"install  {PackageUrl(library, version)}", InstallerEnvironmentVariables()))
+            {
+                if (proc.ExitCode != 0)
+                {
+                    throw new Exception($"'datadog-installer install {ociImageName}' failed with exit code: {proc.ExitCode}");
+                }
+            }
+        }
+
+        public static ActionResult InstallPackages(Session session)
+        {
+            return new InstallOciPackages(new SessionWrapper(session)).InstallPackages();
+        }
+
+        public static ActionResult RollbackActions(Session session)
+        {
+            return new InstallOciPackages(new SessionWrapper(session)).RollbackState();
+        }
+    }
+}
diff --git a/tools/windows/DatadogAgentInstaller/CustomActions/Interfaces/ISession.cs b/tools/windows/DatadogAgentInstaller/CustomActions/Interfaces/ISession.cs
index 3df840d9c898..471b20a74478 100644
--- a/tools/windows/DatadogAgentInstaller/CustomActions/Interfaces/ISession.cs
+++ b/tools/windows/DatadogAgentInstaller/CustomActions/Interfaces/ISession.cs
@@ -1,4 +1,5 @@
 using Microsoft.Deployment.WindowsInstaller;
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 
@@ -44,5 +45,10 @@ void Log(
         /// see <see cref="SessionWrapper.RunCommand"/>
         /// </summary>
         Process RunCommand(string filename, string arguments);
+
+        /// <summary>
+        /// see <see cref="SessionWrapper.RunCommand"/>
+        /// </summary>
+        Process RunCommand(string filename, string arguments, IDictionary<string, string> environment);
     }
 }
diff --git a/tools/windows/DatadogAgentInstaller/CustomActions/Rollback/InstallerPackageRollback.cs b/tools/windows/DatadogAgentInstaller/CustomActions/Rollback/InstallerPackageRollback.cs
new file mode 100644
index 000000000000..b9d373531ad5
--- /dev/null
+++ b/tools/windows/DatadogAgentInstaller/CustomActions/Rollback/InstallerPackageRollback.cs
@@ -0,0 +1,44 @@
+using Datadog.CustomActions.Extensions;
+using Datadog.CustomActions.Interfaces;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Datadog.CustomActions.Rollback
+{
+    public class InstallerPackageRollback : IRollbackAction
+    {
+        [JsonProperty("InstallerCommand")] private string _installerCommand;
+
+        [JsonConstructor]
+        public InstallerPackageRollback()
+        {
+        }
+
+        public InstallerPackageRollback(string installerCommand)
+        {
+            _installerCommand = installerCommand;
+        }
+
+        public void Restore(ISession session, IFileSystemServices fileSystemServices,
+            IServiceController serviceController)
+        {
+            var installDir = session.Property("PROJECTLOCATION");
+            var installerExecutable = System.IO.Path.Combine(installDir, "bin", "datadog-installer.exe");
+
+            var installerEnvVariables = new Dictionary<string, string>();
+            installerEnvVariables["DD_API_KEY"] = session.Property("APIKEY");
+            installerEnvVariables["DD_SITE"] = session.Property("SITE");
+
+            using (var proc = session.RunCommand(installerExecutable, _installerCommand, installerEnvVariables))
+            {
+                if (proc.ExitCode != 0)
+                {
+                    session.Log(
+                        $"error running rollback command {installerExecutable} {_installerCommand} failed with exit code {proc.ExitCode}");
+                }
+            }
+        }
+    }
+}
diff --git a/tools/windows/DatadogAgentInstaller/CustomActions/SessionWrapper.cs b/tools/windows/DatadogAgentInstaller/CustomActions/SessionWrapper.cs
index 60d39b467b2f..ada113dd7784 100644
--- a/tools/windows/DatadogAgentInstaller/CustomActions/SessionWrapper.cs
+++ b/tools/windows/DatadogAgentInstaller/CustomActions/SessionWrapper.cs
@@ -2,6 +2,7 @@
 using Datadog.CustomActions.Native;
 using Microsoft.Deployment.WindowsInstaller;
 using System;
+using System.Collections.Generic;
 using System.Diagnostics;
 
 namespace Datadog.CustomActions
@@ -49,7 +50,7 @@ public IFeatureInfo Feature(string FeatureName)
         /// <summary>
         /// Runs command and logs stdout/stderr to MSI log, returns the process object.
         /// </summary>
-        public Process RunCommand(string filename, string arguments)
+        public Process RunCommand(string filename, string arguments, IDictionary<string, string> environment)
         {
             var psi = new ProcessStartInfo
             {
@@ -60,6 +61,13 @@ public Process RunCommand(string filename, string arguments)
                 FileName = filename,
                 Arguments = arguments
             };
+            if (environment != null)
+            {
+                foreach (var kvp in environment)
+                {
+                    psi.Environment[kvp.Key] = kvp.Value;
+                }
+            }
             var proc = new Process();
             proc.StartInfo = psi;
             proc.OutputDataReceived += (_, args) => Log(args.Data);
@@ -71,6 +79,14 @@ public Process RunCommand(string filename, string arguments)
             return proc;
         }
 
+        /// <summary>
+        /// Runs command and logs stdout/stderr to MSI log, returns the process object.
+        /// </summary>
+        public Process RunCommand(string filename, string arguments)
+        {
+            return RunCommand(filename, arguments, new Dictionary<string, string>());
+        }
+
         public CustomActionData CustomActionData => _session.CustomActionData;
     }
 }
diff --git a/tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentCustomActions.cs b/tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentCustomActions.cs
index 39bba83dc1af..98866811fb71 100644
--- a/tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentCustomActions.cs	
+++ b/tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentCustomActions.cs	
@@ -60,6 +60,10 @@ public class AgentCustomActions
 
         public ManagedAction SendFlare { get; }
 
+        public ManagedAction InstallOciPackages { get; }
+
+        public ManagedAction RollbackOciPackages { get; }
+
         public ManagedAction WriteInstallInfo { get; }
 
         public ManagedAction ReportInstallFailure { get; }
@@ -467,6 +471,39 @@ public AgentCustomActions()
                 Sequence = Sequence.NotInSequence
             };
 
+            RollbackOciPackages = new CustomAction<CustomActions>(
+                    new Id(nameof(RollbackOciPackages)),
+                    CustomActions.RollbackOciPackages,
+                    Return.ignore,
+                    When.Before,
+                    Step.StartServices,
+                    Condition.NOT(Conditions.Uninstalling | Conditions.RemovingForUpgrade)
+            )
+            {
+                Execute = Execute.rollback,
+                Impersonate = false
+            }
+            .SetProperties("PROJECTLOCATION=[PROJECTLOCATION],SITE=[SITE],APIKEY=[APIKEY]");
+
+            InstallOciPackages = new CustomAction<CustomActions>(
+                    new Id(nameof(InstallOciPackages)),
+                    CustomActions.InstallOciPackages,
+                    Return.check,
+                    When.Before,
+                    Step.StartServices,
+                    Condition.NOT(Conditions.Uninstalling | Conditions.RemovingForUpgrade)
+            )
+            {
+                Execute = Execute.deferred,
+                Impersonate = false
+            }
+            .SetProperties("PROJECTLOCATION=[PROJECTLOCATION]," +
+                           "APIKEY=[APIKEY]," +
+                           "SITE=[SITE]," +
+                           "DD_INSTALLER_REGISTRY_URL=[DD_INSTALLER_REGISTRY_URL]," +
+                           "DD_APM_INSTRUMENTATION_ENABLED=[DD_APM_INSTRUMENTATION_ENABLED]," +
+                           "DD_APM_INSTRUMENTATION_LIBRARIES=[DD_APM_INSTRUMENTATION_LIBRARIES]");
+
             WriteInstallInfo = new CustomAction<CustomActions>(
                     new Id(nameof(WriteInstallInfo)),
                     CustomActions.WriteInstallInfo,
diff --git a/tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentInstaller.cs b/tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentInstaller.cs
index 4aedbb1f5a43..da6aa378aae6 100644
--- a/tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentInstaller.cs	
+++ b/tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentInstaller.cs	
@@ -117,6 +117,18 @@ public Project Configure()
                 // execute the install custom action rollback; otherwise it won't.
                 new Property("DDDRIVERROLLBACK_NPM", "1"),
                 new Property("DDDRIVERROLLBACK_PROCMON", "1"),
+                new Property("DD_APM_INSTRUMENTATION_ENABLED")
+                {
+                    AttributesDefinition = "Secure=yes"
+                },
+                new Property("DD_APM_INSTRUMENTATION_LIBRARIES")
+                {
+                    AttributesDefinition = "Secure=yes"
+                },
+                new Property("DD_INSTALLER_REGISTRY_URL")
+                {
+                    AttributesDefinition = "Secure=yes"
+                },
                 // Add a checkbox at the end of the setup to launch the Datadog Agent Manager
                 new LaunchCustomApplicationFromExitDialog(
                     _agentBinaries.TrayId,


Reviews:
user: login: bmermet
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T11:03:28Z
user: login: clarkb7
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T14:30:00Z
user: login: jhgilbert
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T19:20:37Z
user: login: clarkb7
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T21:04:22Z
user: login: jack0x2
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T22:42:36Z
user: login: jack0x2
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T22:43:12Z

Review comments:
diff_hunk: @@ -0,0 +1,363 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016-present Datadog, Inc.
+
+package dotnettests
+
+import (
+	_ "embed"
+	"fmt"
+	"os"
+
+	"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e"
+	winawshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/provisioners/aws/host/windows"
+	installerwindows "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows"
+	"github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows/consts"
+
+	"testing"
+)
+
+type testAgentMSIInstallsDotnetLibrary struct {
+	baseIISSuite
+	previousDotnetLibraryVersion installerwindows.PackageVersion
+	currentDotnetLibraryVersion  installerwindows.PackageVersion
+}
+
+// TestDotnetInstalls tests the usage of the Datadog installer and the MSI to install the apm-library-dotnet-package package.
+func TestAgentMSIInstallsDotnetLibrary(t *testing.T) {
+	e2e.Run(t, &testAgentMSIInstallsDotnetLibrary{},
+		e2e.WithProvisioner(
+			winawshost.ProvisionerNoAgentNoFakeIntake()))
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) SetupSuite() {
+	s.baseIISSuite.SetupSuite()
+
+	s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("PREVIOUS_DOTNET_VERSION_PACKAGE"))
+	if s.previousDotnetLibraryVersion.PackageVersion() == "" {
+		s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58926677.beta.sha-af5a1fab-1")
+	}
+	s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("CURRENT_DOTNET_VERSION_PACKAGE"))
+	if s.currentDotnetLibraryVersion.PackageVersion() == "" {
+		s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58951229.beta.sha-af5a1fab-1")
+	}
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) AfterTest(suiteName, testName string) {
+	s.Installer().Purge()
+	s.baseIISSuite.AfterTest(suiteName, testName)
+}
+
+// TestInstallFromMSI tests the Agent MSI can install the dotnet library OCI package
+func (s *testAgentMSIInstallsDotnetLibrary) TestInstallFromMSI() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Assert
+	s.assertSuccessfulPromoteExperiment(version.Version())
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+// TestMSIThenRemoteUpgrade tests the dotnet library can be remotely upgraded from an Agent MSI installed version
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIThenRemoteUpgrade() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Start remote upgrade experiment
+	_, err := s.startExperimentCurrentVersion()
+	s.Require().NoError(err)
+	s.assertSuccessfulStartExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+
+	// Promote the experiment
+	_, err = s.Installer().PromoteExperiment("datadog-apm-library-dotnet")
+	s.Require().NoError(err)
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+}
+
+// TestUpgradeWithMSI tests the dotnet library can be upgraded from the MSI
+func (s *testAgentMSIInstallsDotnetLibrary) TestUpgradeWithMSI() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Install the second version using a new Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("upgrade.log"),
+	)
+
+	// Check that the new version became the stable version
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+}
+
+// TestMSIRollbackRemovesLibrary tests that the dotnet library is removed when the MSI installation fails
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIRollbackRemovesLibrary() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Assert
+	s.Require().Host(s.Env().RemoteHost).
+		NoDirExists(`C:/ProgramData/Datadog/Installer/packages/datadog-apm-library-dotnet`)
+}
+
+// TestMSISkipRollbackIfInstalled tests that the newly installed dotnet library is not removed when the MSI upgrade fails
+// if another version of the library was already installed.
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSISkipRollbackIfInstalled() {
+	// Arrange
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Rollback during upgrade to the new version
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Start IIS and check that the NEW version of the library is loaded
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+}
+
+// TestUninstallKeepsLibrary tests that the dotnet library is not removed when the Agent MSI is uninstalled
+func (s *testAgentMSIInstallsDotnetLibrary) TestUninstallKeepsLibrary() {
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Install the dotnet library with the Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Uninstall the Agent
+	err := s.Installer().Uninstall(
+		installerwindows.WithMSILogFile("uninstall.log"),
+	)
+	s.Require().NoError(err)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) setAgentConfig() {
path: test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/msi_install_test.go
user: login: bmermet
body: Why is this needed?
created_at: 2025-03-19T11:03:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,363 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016-present Datadog, Inc.
+
+package dotnettests
+
+import (
+	_ "embed"
+	"fmt"
+	"os"
+
+	"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e"
+	winawshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/provisioners/aws/host/windows"
+	installerwindows "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows"
+	"github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows/consts"
+
+	"testing"
+)
+
+type testAgentMSIInstallsDotnetLibrary struct {
+	baseIISSuite
+	previousDotnetLibraryVersion installerwindows.PackageVersion
+	currentDotnetLibraryVersion  installerwindows.PackageVersion
+}
+
+// TestDotnetInstalls tests the usage of the Datadog installer and the MSI to install the apm-library-dotnet-package package.
+func TestAgentMSIInstallsDotnetLibrary(t *testing.T) {
+	e2e.Run(t, &testAgentMSIInstallsDotnetLibrary{},
+		e2e.WithProvisioner(
+			winawshost.ProvisionerNoAgentNoFakeIntake()))
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) SetupSuite() {
+	s.baseIISSuite.SetupSuite()
+
+	s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("PREVIOUS_DOTNET_VERSION_PACKAGE"))
+	if s.previousDotnetLibraryVersion.PackageVersion() == "" {
+		s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58926677.beta.sha-af5a1fab-1")
+	}
+	s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("CURRENT_DOTNET_VERSION_PACKAGE"))
+	if s.currentDotnetLibraryVersion.PackageVersion() == "" {
+		s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58951229.beta.sha-af5a1fab-1")
+	}
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) AfterTest(suiteName, testName string) {
+	s.Installer().Purge()
+	s.baseIISSuite.AfterTest(suiteName, testName)
+}
+
+// TestInstallFromMSI tests the Agent MSI can install the dotnet library OCI package
+func (s *testAgentMSIInstallsDotnetLibrary) TestInstallFromMSI() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Assert
+	s.assertSuccessfulPromoteExperiment(version.Version())
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+// TestMSIThenRemoteUpgrade tests the dotnet library can be remotely upgraded from an Agent MSI installed version
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIThenRemoteUpgrade() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Start remote upgrade experiment
+	_, err := s.startExperimentCurrentVersion()
+	s.Require().NoError(err)
+	s.assertSuccessfulStartExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+
+	// Promote the experiment
+	_, err = s.Installer().PromoteExperiment("datadog-apm-library-dotnet")
+	s.Require().NoError(err)
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+}
+
+// TestUpgradeWithMSI tests the dotnet library can be upgraded from the MSI
+func (s *testAgentMSIInstallsDotnetLibrary) TestUpgradeWithMSI() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Install the second version using a new Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("upgrade.log"),
+	)
+
+	// Check that the new version became the stable version
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+}
+
+// TestMSIRollbackRemovesLibrary tests that the dotnet library is removed when the MSI installation fails
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIRollbackRemovesLibrary() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Assert
+	s.Require().Host(s.Env().RemoteHost).
+		NoDirExists(`C:/ProgramData/Datadog/Installer/packages/datadog-apm-library-dotnet`)
+}
+
+// TestMSISkipRollbackIfInstalled tests that the newly installed dotnet library is not removed when the MSI upgrade fails
+// if another version of the library was already installed.
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSISkipRollbackIfInstalled() {
+	// Arrange
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Rollback during upgrade to the new version
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Start IIS and check that the NEW version of the library is loaded
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+}
+
+// TestUninstallKeepsLibrary tests that the dotnet library is not removed when the Agent MSI is uninstalled
+func (s *testAgentMSIInstallsDotnetLibrary) TestUninstallKeepsLibrary() {
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Install the dotnet library with the Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Uninstall the Agent
+	err := s.Installer().Uninstall(
+		installerwindows.WithMSILogFile("uninstall.log"),
+	)
+	s.Require().NoError(err)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) setAgentConfig() {
path: test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/msi_install_test.go
user: login: clarkb7
body: The tests use the daemon to perform remote upgrades, so we have to enable the daemon in the config.
created_at: 2025-03-19T14:29:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,363 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016-present Datadog, Inc.
+
+package dotnettests
+
+import (
+	_ "embed"
+	"fmt"
+	"os"
+
+	"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e"
+	winawshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/provisioners/aws/host/windows"
+	installerwindows "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows"
+	"github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows/consts"
+
+	"testing"
+)
+
+type testAgentMSIInstallsDotnetLibrary struct {
+	baseIISSuite
+	previousDotnetLibraryVersion installerwindows.PackageVersion
+	currentDotnetLibraryVersion  installerwindows.PackageVersion
+}
+
+// TestDotnetInstalls tests the usage of the Datadog installer and the MSI to install the apm-library-dotnet-package package.
+func TestAgentMSIInstallsDotnetLibrary(t *testing.T) {
+	e2e.Run(t, &testAgentMSIInstallsDotnetLibrary{},
+		e2e.WithProvisioner(
+			winawshost.ProvisionerNoAgentNoFakeIntake()))
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) SetupSuite() {
+	s.baseIISSuite.SetupSuite()
+
+	s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("PREVIOUS_DOTNET_VERSION_PACKAGE"))
+	if s.previousDotnetLibraryVersion.PackageVersion() == "" {
+		s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58926677.beta.sha-af5a1fab-1")
+	}
+	s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("CURRENT_DOTNET_VERSION_PACKAGE"))
+	if s.currentDotnetLibraryVersion.PackageVersion() == "" {
+		s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58951229.beta.sha-af5a1fab-1")
+	}
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) AfterTest(suiteName, testName string) {
+	s.Installer().Purge()
+	s.baseIISSuite.AfterTest(suiteName, testName)
+}
+
+// TestInstallFromMSI tests the Agent MSI can install the dotnet library OCI package
+func (s *testAgentMSIInstallsDotnetLibrary) TestInstallFromMSI() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Assert
+	s.assertSuccessfulPromoteExperiment(version.Version())
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+// TestMSIThenRemoteUpgrade tests the dotnet library can be remotely upgraded from an Agent MSI installed version
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIThenRemoteUpgrade() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Start remote upgrade experiment
+	_, err := s.startExperimentCurrentVersion()
+	s.Require().NoError(err)
+	s.assertSuccessfulStartExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+
+	// Promote the experiment
+	_, err = s.Installer().PromoteExperiment("datadog-apm-library-dotnet")
+	s.Require().NoError(err)
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+}
+
+// TestUpgradeWithMSI tests the dotnet library can be upgraded from the MSI
+func (s *testAgentMSIInstallsDotnetLibrary) TestUpgradeWithMSI() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Install the second version using a new Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("upgrade.log"),
+	)
+
+	// Check that the new version became the stable version
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+}
+
+// TestMSIRollbackRemovesLibrary tests that the dotnet library is removed when the MSI installation fails
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIRollbackRemovesLibrary() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Assert
+	s.Require().Host(s.Env().RemoteHost).
+		NoDirExists(`C:/ProgramData/Datadog/Installer/packages/datadog-apm-library-dotnet`)
+}
+
+// TestMSISkipRollbackIfInstalled tests that the newly installed dotnet library is not removed when the MSI upgrade fails
+// if another version of the library was already installed.
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSISkipRollbackIfInstalled() {
+	// Arrange
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Rollback during upgrade to the new version
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Start IIS and check that the NEW version of the library is loaded
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+}
+
+// TestUninstallKeepsLibrary tests that the dotnet library is not removed when the Agent MSI is uninstalled
+func (s *testAgentMSIInstallsDotnetLibrary) TestUninstallKeepsLibrary() {
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Install the dotnet library with the Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Uninstall the Agent
+	err := s.Installer().Uninstall(
+		installerwindows.WithMSILogFile("uninstall.log"),
+	)
+	s.Require().NoError(err)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) setAgentConfig() {
+	s.Env().RemoteHost.MkdirAll("C:\\ProgramData\\Datadog")
+	s.Env().RemoteHost.WriteFile(consts.ConfigPath, []byte(`
+api_key: aaaaaaaaa
+remote_updates: true
+`))
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) assertSuccessfulStartExperiment(version string) {
+	s.Require().Host(s.Env().RemoteHost).HasDatadogInstaller().Status().
+		HasPackage("datadog-apm-library-dotnet").
+		WithExperimentVersionMatchPredicate(func(actual string) {
+			s.Require().Contains(actual, version)
+		})
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) assertSuccessfulPromoteExperiment(version string) {
+	s.Require().Host(s.Env().RemoteHost).HasDatadogInstaller().Status().
+		HasPackage("datadog-apm-library-dotnet").
+		WithStableVersionMatchPredicate(func(actual string) {
+			s.Require().Contains(actual, version)
+		}).
+		WithExperimentVersionEqual("")
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) startExperimentCurrentVersion() (string, error) {
+	return s.startExperimentWithCustomPackage(installerwindows.WithName("datadog-apm-library-dotnet"),
+		installerwindows.WithAlias("apm-library-dotnet-package"),
+		// TODO remove override once image is published in prod
+		installerwindows.WithVersion(s.currentDotnetLibraryVersion.PackageVersion()),
+		installerwindows.WithRegistry("install.datad0g.com"),
+		installerwindows.WithDevEnvOverrides("CURRENT_DOTNET_LIBRARY"),
+	)
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) startExperimentWithCustomPackage(opts ...installerwindows.PackageOption) (string, error) {
path: test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/msi_install_test.go
user: login: jack0x2
body: As a todo for later. We should merge this together with the base-suite ones to not have copies. 
created_at: 2025-03-19T22:42:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,363 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016-present Datadog, Inc.
+
+package dotnettests
+
+import (
+	_ "embed"
+	"fmt"
+	"os"
+
+	"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e"
+	winawshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/provisioners/aws/host/windows"
+	installerwindows "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows"
+	"github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/windows/consts"
+
+	"testing"
+)
+
+type testAgentMSIInstallsDotnetLibrary struct {
+	baseIISSuite
+	previousDotnetLibraryVersion installerwindows.PackageVersion
+	currentDotnetLibraryVersion  installerwindows.PackageVersion
+}
+
+// TestDotnetInstalls tests the usage of the Datadog installer and the MSI to install the apm-library-dotnet-package package.
+func TestAgentMSIInstallsDotnetLibrary(t *testing.T) {
+	e2e.Run(t, &testAgentMSIInstallsDotnetLibrary{},
+		e2e.WithProvisioner(
+			winawshost.ProvisionerNoAgentNoFakeIntake()))
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) SetupSuite() {
+	s.baseIISSuite.SetupSuite()
+
+	s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("PREVIOUS_DOTNET_VERSION_PACKAGE"))
+	if s.previousDotnetLibraryVersion.PackageVersion() == "" {
+		s.previousDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58926677.beta.sha-af5a1fab-1")
+	}
+	s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion(os.Getenv("CURRENT_DOTNET_VERSION_PACKAGE"))
+	if s.currentDotnetLibraryVersion.PackageVersion() == "" {
+		s.currentDotnetLibraryVersion = installerwindows.NewVersionFromPackageVersion("3.13.0-pipeline.58951229.beta.sha-af5a1fab-1")
+	}
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) AfterTest(suiteName, testName string) {
+	s.Installer().Purge()
+	s.baseIISSuite.AfterTest(suiteName, testName)
+}
+
+// TestInstallFromMSI tests the Agent MSI can install the dotnet library OCI package
+func (s *testAgentMSIInstallsDotnetLibrary) TestInstallFromMSI() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Assert
+	s.assertSuccessfulPromoteExperiment(version.Version())
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+// TestMSIThenRemoteUpgrade tests the dotnet library can be remotely upgraded from an Agent MSI installed version
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIThenRemoteUpgrade() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Start remote upgrade experiment
+	_, err := s.startExperimentCurrentVersion()
+	s.Require().NoError(err)
+	s.assertSuccessfulStartExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+
+	// Promote the experiment
+	_, err = s.Installer().PromoteExperiment("datadog-apm-library-dotnet")
+	s.Require().NoError(err)
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+}
+
+// TestUpgradeWithMSI tests the dotnet library can be upgraded from the MSI
+func (s *testAgentMSIInstallsDotnetLibrary) TestUpgradeWithMSI() {
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	s.assertSuccessfulPromoteExperiment(oldVersion.Version())
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, oldVersion.Version())
+
+	// Install the second version using a new Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("upgrade.log"),
+	)
+
+	// Check that the new version became the stable version
+	s.assertSuccessfulPromoteExperiment(newVersion.Version())
+
+	// Check that the old version of the library is still loaded since we have not restarted yet
+	oldLibraryPathAgain := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPathAgain, oldVersion.Version())
+	s.Require().Equal(oldLibraryPath, oldLibraryPathAgain)
+
+	// Restart the IIS application
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the new version of the library is loaded
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+	s.Require().NotEqual(oldLibraryPath, newLibraryPath)
+}
+
+// TestMSIRollbackRemovesLibrary tests that the dotnet library is removed when the MSI installation fails
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSIRollbackRemovesLibrary() {
+	// Arrange
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Act
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Assert
+	s.Require().Host(s.Env().RemoteHost).
+		NoDirExists(`C:/ProgramData/Datadog/Installer/packages/datadog-apm-library-dotnet`)
+}
+
+// TestMSISkipRollbackIfInstalled tests that the newly installed dotnet library is not removed when the MSI upgrade fails
+// if another version of the library was already installed.
+func (s *testAgentMSIInstallsDotnetLibrary) TestMSISkipRollbackIfInstalled() {
+	// Arrange
+	s.setAgentConfig()
+	oldVersion := s.previousDotnetLibraryVersion
+	newVersion := s.currentDotnetLibraryVersion
+
+	// Install first version
+	s.installPreviousAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", oldVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Rollback during upgrade to the new version
+	err := s.Installer().Install(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		// TODO: support DD_INSTALLER_REGISTRY_URL
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", newVersion.PackageVersion())),
+		installerwindows.WithMSILogFile("install-rollback.log"),
+		installerwindows.WithMSIArg("WIXFAILWHENDEFERRED=1"),
+		installerwindows.WithMSIDevEnvOverrides("CURRENT_AGENT"),
+	)
+	s.Require().Error(err)
+
+	// Start IIS and check that the NEW version of the library is loaded
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+	newLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(newLibraryPath, newVersion.Version())
+}
+
+// TestUninstallKeepsLibrary tests that the dotnet library is not removed when the Agent MSI is uninstalled
+func (s *testAgentMSIInstallsDotnetLibrary) TestUninstallKeepsLibrary() {
+	s.setAgentConfig()
+	version := s.currentDotnetLibraryVersion
+
+	// Install the dotnet library with the Agent MSI
+	s.installCurrentAgentVersion(
+		installerwindows.WithMSIArg("DD_APM_INSTRUMENTATION_ENABLED=iis"),
+		// TODO: remove override once image is published in prod
+		installerwindows.WithMSIArg("SITE=datad0g.com"),
+		installerwindows.WithMSIArg(fmt.Sprintf("DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:%s", version.PackageVersion())),
+		installerwindows.WithMSILogFile("install.log"),
+	)
+
+	// Uninstall the Agent
+	err := s.Installer().Uninstall(
+		installerwindows.WithMSILogFile("uninstall.log"),
+	)
+	s.Require().NoError(err)
+
+	// Start the IIS app to load the library
+	defer s.stopIISApp()
+	s.startIISApp(webConfigFile, aspxFile)
+
+	// Check that the expected version of the library is loaded
+	oldLibraryPath := s.getLibraryPathFromInstrumentedIIS()
+	s.Require().Contains(oldLibraryPath, version.Version())
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) setAgentConfig() {
+	s.Env().RemoteHost.MkdirAll("C:\\ProgramData\\Datadog")
+	s.Env().RemoteHost.WriteFile(consts.ConfigPath, []byte(`
+api_key: aaaaaaaaa
+remote_updates: true
+`))
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) assertSuccessfulStartExperiment(version string) {
+	s.Require().Host(s.Env().RemoteHost).HasDatadogInstaller().Status().
+		HasPackage("datadog-apm-library-dotnet").
+		WithExperimentVersionMatchPredicate(func(actual string) {
+			s.Require().Contains(actual, version)
+		})
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) assertSuccessfulPromoteExperiment(version string) {
+	s.Require().Host(s.Env().RemoteHost).HasDatadogInstaller().Status().
+		HasPackage("datadog-apm-library-dotnet").
+		WithStableVersionMatchPredicate(func(actual string) {
+			s.Require().Contains(actual, version)
+		}).
+		WithExperimentVersionEqual("")
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) startExperimentCurrentVersion() (string, error) {
+	return s.startExperimentWithCustomPackage(installerwindows.WithName("datadog-apm-library-dotnet"),
+		installerwindows.WithAlias("apm-library-dotnet-package"),
+		// TODO remove override once image is published in prod
+		installerwindows.WithVersion(s.currentDotnetLibraryVersion.PackageVersion()),
+		installerwindows.WithRegistry("install.datad0g.com"),
+		installerwindows.WithDevEnvOverrides("CURRENT_DOTNET_LIBRARY"),
+	)
+}
+
+func (s *testAgentMSIInstallsDotnetLibrary) startExperimentWithCustomPackage(opts ...installerwindows.PackageOption) (string, error) {
path: test/new-e2e/tests/installer/windows/suites/apm-library-dotnet-package/msi_install_test.go
user: login: jack0x2
body: But not forget to keep this one as `StartExperiment` not `StartInstallerExperiment` :) 
created_at: 2025-03-19T22:43:12Z
author_association: CONTRIBUTOR

PR comments:
user: login: agent-platform-auto-pr[bot]
created_at: 2025-03-11T17:13:34Z
body: ## Uncompressed package size comparison

Comparison with [ancestor](https://github.com/DataDog/datadog-agent/commit/418e2bea5d9089194d2e9392e4ddb93e9fb1912b) `418e2bea5d9089194d2e9392e4ddb93e9fb1912b`


<details>
  <summary> Diff per package </summary>

|package|diff|status|size|ancestor|threshold|
|--|--|--|--|--|--|
|datadog-agent-amd64-deb|0.00MB|✅|807.56MB|807.56MB|0.50MB|
|datadog-agent-x86_64-rpm|0.00MB|✅|817.36MB|817.36MB|0.50MB|
|datadog-agent-x86_64-suse|0.00MB|✅|817.36MB|817.36MB|0.50MB|
|datadog-agent-arm64-deb|0.00MB|✅|798.56MB|798.56MB|0.50MB|
|datadog-agent-aarch64-rpm|0.00MB|✅|808.33MB|808.33MB|0.50MB|
|datadog-dogstatsd-amd64-deb|0.00MB|✅|39.34MB|39.34MB|0.50MB|
|datadog-dogstatsd-x86_64-rpm|0.00MB|✅|39.42MB|39.42MB|0.50MB|
|datadog-dogstatsd-x86_64-suse|0.00MB|✅|39.42MB|39.42MB|0.50MB|
|datadog-dogstatsd-arm64-deb|0.00MB|✅|37.87MB|37.87MB|0.50MB|
|datadog-heroku-agent-amd64-deb|0.00MB|✅|444.37MB|444.37MB|0.50MB|
|datadog-iot-agent-amd64-deb|0.00MB|✅|62.23MB|62.23MB|0.50MB|
|datadog-iot-agent-x86_64-rpm|0.00MB|✅|62.30MB|62.30MB|0.50MB|
|datadog-iot-agent-x86_64-suse|0.00MB|✅|62.30MB|62.30MB|0.50MB|
|datadog-iot-agent-arm64-deb|0.00MB|✅|59.46MB|59.46MB|0.50MB|
|datadog-iot-agent-aarch64-rpm|0.00MB|✅|59.53MB|59.53MB|0.50MB|

</details>

## Decision
✅ Passed



author_association: CONTRIBUTOR
user: login: agent-platform-auto-pr[bot]
created_at: 2025-03-11T17:17:52Z
body: ## Static quality checks ✅

Please find below the results from static quality gates


<details>
<summary>Successful checks</summary>

### Info

|Result|Quality gate|On disk size|On disk size limit|On wire size|On wire size limit|
|----|----|----|----|----|----|
|✅|static_quality_gate_agent_deb_amd64|781.69 MiB|801.8 MiB|190.74 MiB|202.62 MiB|
|✅|static_quality_gate_agent_deb_arm64|772.94 MiB|793.14 MiB|172.92 MiB|184.51 MiB|
|✅|static_quality_gate_agent_rpm_amd64|781.59 MiB|801.79 MiB|193.33 MiB|205.03 MiB|
|✅|static_quality_gate_agent_rpm_arm64|772.96 MiB|793.09 MiB|174.54 MiB|186.44 MiB|
|✅|static_quality_gate_agent_suse_amd64|781.69 MiB|801.81 MiB|193.33 MiB|205.03 MiB|
|✅|static_quality_gate_agent_suse_arm64|773.06 MiB|793.14 MiB|174.54 MiB|186.44 MiB|
|✅|static_quality_gate_dogstatsd_deb_amd64|37.59 MiB|47.67 MiB|9.75 MiB|19.78 MiB|
|✅|static_quality_gate_dogstatsd_deb_arm64|36.19 MiB|46.27 MiB|8.45 MiB|18.49 MiB|
|✅|static_quality_gate_dogstatsd_rpm_amd64|37.59 MiB|47.67 MiB|9.75 MiB|19.79 MiB|
|✅|static_quality_gate_dogstatsd_suse_amd64|37.59 MiB|47.67 MiB|9.75 MiB|19.79 MiB|
|✅|static_quality_gate_iot_agent_deb_amd64|59.43 MiB|69.0 MiB|14.92 MiB|24.8 MiB|
|✅|static_quality_gate_iot_agent_deb_arm64|56.78 MiB|66.4 MiB|12.86 MiB|22.8 MiB|
|✅|static_quality_gate_iot_agent_rpm_amd64|59.43 MiB|69.0 MiB|14.95 MiB|24.8 MiB|
|✅|static_quality_gate_iot_agent_rpm_arm64|56.78 MiB|66.4 MiB|12.88 MiB|22.8 MiB|
|✅|static_quality_gate_iot_agent_suse_amd64|59.43 MiB|69.0 MiB|14.95 MiB|24.8 MiB|
|✅|static_quality_gate_docker_agent_amd64|866.3 MiB|886.12 MiB|291.43 MiB|304.21 MiB|
|✅|static_quality_gate_docker_agent_arm64|880.98 MiB|900.79 MiB|277.85 MiB|290.47 MiB|
|✅|static_quality_gate_docker_agent_jmx_amd64|1.04 GiB|1.06 GiB|366.53 MiB|379.33 MiB|
|✅|static_quality_gate_docker_agent_jmx_arm64|1.04 GiB|1.06 GiB|348.94 MiB|361.55 MiB|
|✅|static_quality_gate_docker_dogstatsd_amd64|45.73 MiB|55.78 MiB|17.25 MiB|27.28 MiB|
|✅|static_quality_gate_docker_dogstatsd_arm64|44.36 MiB|54.45 MiB|16.12 MiB|26.16 MiB|
|✅|static_quality_gate_docker_cluster_agent_amd64|264.38 MiB|274.78 MiB|106.07 MiB|116.28 MiB|
|✅|static_quality_gate_docker_cluster_agent_arm64|280.34 MiB|290.82 MiB|100.9 MiB|111.12 MiB|

</details>

author_association: CONTRIBUTOR
user: login: cit-pr-commenter[bot]
created_at: 2025-03-11T18:17:50Z
body: ## Regression Detector

# Regression Detector Results

[Metrics dashboard](https://app.datadoghq.com/dashboard/ykh-ua8-vcu/SMP-Regression-Detector-Metrics?fromUser=true&refresh_mode=paused&tpl_var_run-id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&view=spans&from_ts=1742419237000&to_ts=1742419837000&live=false)  
[Target profiles](https://app.datadoghq.com/profiling/explorer?query=env%3Asingle-machine-performance%20service%3Adatadog-agent%20job_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&viz=stream&start=1742412037000&end=1742423437000&paused=true)  
Run ID: 15e1187d-7e6d-49fb-8204-f5e023619788

Baseline: 418e2bea5d9089194d2e9392e4ddb93e9fb1912b
Comparison: 68f3db50e76900bfabcca0a60c0039d95c1114fb
[Diff](418e2bea5d9089194d2e9392e4ddb93e9fb1912b...68f3db50e76900bfabcca0a60c0039d95c1114fb)


## Optimization Goals: ✅ No significant changes detected


<details>
<summary><h2>
Fine details of change detection per experiment
</h2></summary>

| perf | experiment                                   | goal               | Δ mean % | Δ mean % CI    | trials | links                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
|------|----------------------------------------------|--------------------|----------|----------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ➖   | quality_gate_logs                            | % cpu utilization  | +2.74    | [-0.11, +5.60] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Aquality_gate_logs%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                                          |
| ➖   | quality_gate_idle                            | memory utilization | +0.85    | [+0.78, +0.92] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Aquality_gate_idle%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false) [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle&tpl_var_job_id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&tpl_var_run-id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&view=spans&from_ts=1742419237000&to_ts=1742419837000&live=false)                           |
| ➖   | quality_gate_idle_all_features               | memory utilization | +0.40    | [+0.32, +0.47] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Aquality_gate_idle_all_features%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false) [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle_all_features&tpl_var_job_id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&tpl_var_run-id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&view=spans&from_ts=1742419237000&to_ts=1742419837000&live=false) |
| ➖   | file_to_blackhole_500ms_latency              | egress throughput  | +0.06    | [-0.72, +0.84] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_500ms_latency%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                            |
| ➖   | file_to_blackhole_100ms_latency              | egress throughput  | +0.01    | [-0.70, +0.72] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_100ms_latency%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                            |
| ➖   | file_to_blackhole_0ms_latency_http1          | egress throughput  | +0.00    | [-0.85, +0.85] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_0ms_latency_http1%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                        |
| ➖   | tcp_dd_logs_filter_exclude                   | ingress throughput | -0.00    | [-0.01, +0.01] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Atcp_dd_logs_filter_exclude%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                                 |
| ➖   | uds_dogstatsd_to_api                         | ingress throughput | -0.01    | [-0.31, +0.29] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Auds_dogstatsd_to_api%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                                       |
| ➖   | file_to_blackhole_300ms_latency              | egress throughput  | -0.02    | [-0.65, +0.61] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_300ms_latency%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                            |
| ➖   | file_to_blackhole_0ms_latency                | egress throughput  | -0.02    | [-0.79, +0.75] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_0ms_latency%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                              |
| ➖   | file_to_blackhole_0ms_latency_http2          | egress throughput  | -0.02    | [-0.83, +0.80] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_0ms_latency_http2%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                        |
| ➖   | file_tree                                    | memory utilization | -0.07    | [-0.21, +0.07] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_tree%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                                                  |
| ➖   | file_to_blackhole_1000ms_latency             | egress throughput  | -0.08    | [-0.85, +0.69] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_1000ms_latency%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                           |
| ➖   | file_to_blackhole_1000ms_latency_linear_load | egress throughput  | -0.08    | [-0.54, +0.38] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_1000ms_latency_linear_load%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                               |
| ➖   | uds_dogstatsd_to_api_cpu                     | % cpu utilization  | -0.55    | [-1.37, +0.27] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Auds_dogstatsd_to_api_cpu%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                                   |
| ➖   | uds_dogstatsd_20mb_12k_contexts_20_senders   | memory utilization | -0.58    | [-0.65, -0.51] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Auds_dogstatsd_20mb_12k_contexts_20_senders%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                 |
| ➖   | tcp_syslog_to_blackhole                      | ingress throughput | -0.70    | [-0.76, -0.63] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Atcp_syslog_to_blackhole%20run_id%3A15e1187d-7e6d-49fb-8204-f5e023619788&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1742412037000&to_ts=1742423437000&live=false)                                                                                                                                                                                                                                                                                                                                                                    |
</details>


<details>
<summary><h2>
Bounds Checks: ✅ Passed
</h2></summary>

| perf | experiment                                   | bounds_check_name  | replicates_passed | links                                                                                                                                                                                                                                                                                                                                                       |
|------|----------------------------------------------|--------------------|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ✅   | file_to_blackhole_0ms_latency                | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_0ms_latency                | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_0ms_latency_http1          | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_0ms_latency_http1          | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_0ms_latency_http2          | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_0ms_latency_http2          | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_1000ms_latency             | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_1000ms_latency_linear_load | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_100ms_latency              | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_100ms_latency              | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_300ms_latency              | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_300ms_latency              | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_500ms_latency              | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | file_to_blackhole_500ms_latency              | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | quality_gate_idle                            | intake_connections | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle&tpl_var_job_id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&tpl_var_run-id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&view=spans&from_ts=1742419237000&to_ts=1742419837000&live=false)              |
| ✅   | quality_gate_idle                            | memory_usage       | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle&tpl_var_job_id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&tpl_var_run-id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&view=spans&from_ts=1742419237000&to_ts=1742419837000&live=false)              |
| ✅   | quality_gate_idle_all_features               | intake_connections | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle_all_features&tpl_var_job_id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&tpl_var_run-id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&view=spans&from_ts=1742419237000&to_ts=1742419837000&live=false) |
| ✅   | quality_gate_idle_all_features               | memory_usage       | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle_all_features&tpl_var_job_id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&tpl_var_run-id%5B0%5D=15e1187d-7e6d-49fb-8204-f5e023619788&view=spans&from_ts=1742419237000&to_ts=1742419837000&live=false) |
| ✅   | quality_gate_logs                            | intake_connections | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | quality_gate_logs                            | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
| ✅   | quality_gate_logs                            | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                                                                             |
</details>


<details>
<summary><h2>
Explanation
</h2></summary>

**Confidence level:** 90.00%
**Effect size tolerance:** |Δ mean %| ≥ 5.00%

Performance changes are noted in the **perf** column of each table:

* ✅ = significantly better comparison variant performance
* ❌ = significantly worse comparison variant performance
* ➖ = no significant change in performance

A regression test is an A/B test of target performance in a repeatable rig, where "performance" is measured as "comparison variant minus baseline variant" for an optimization goal (e.g., ingress throughput). Due to intrinsic variability in measuring that goal, we can only estimate its mean value for each experiment; we report uncertainty in that value as a 90.00% confidence interval denoted "Δ mean % CI".

For each experiment, we decide whether a change in performance is a "regression" -- a change worth investigating further -- if all of the following criteria are true:

1. Its estimated |Δ mean %| ≥ 5.00%, indicating the change is big enough to merit a closer look.

2. Its 90.00% confidence interval "Δ mean % CI" does not contain zero, indicating that *if our statistical model is accurate*, there is at least a 90.00% chance there is a difference in performance between baseline and comparison variants.

3. Its configuration does not mark it "erratic".
</details>

## CI Pass/Fail Decision

✅ **Passed.** All Quality Gates passed.

- **quality_gate_idle_all_features**, bounds check **intake_connections**: 10/10 replicas passed. Gate passed.
- **quality_gate_idle_all_features**, bounds check **memory_usage**: 10/10 replicas passed. Gate passed.
- **quality_gate_logs**, bounds check **lost_bytes**: 10/10 replicas passed. Gate passed.
- **quality_gate_logs**, bounds check **intake_connections**: 10/10 replicas passed. Gate passed.
- **quality_gate_logs**, bounds check **memory_usage**: 10/10 replicas passed. Gate passed.
- **quality_gate_idle**, bounds check **memory_usage**: 10/10 replicas passed. Gate passed.
- **quality_gate_idle**, bounds check **intake_connections**: 10/10 replicas passed. Gate passed.
author_association: NONE
user: login: agent-platform-auto-pr[bot]
created_at: 2025-03-18T21:29:07Z
body: ## Gitlab CI Configuration Changes

### Modified Jobs
<details>
<summary><b>new-e2e-installer-windows</b></summary>

```diff
  new-e2e-installer-windows:
    after_script:
    - $CI_PROJECT_DIR/tools/ci/junit_upload.sh
    artifacts:
      expire_in: 2 weeks
      paths:
      - $E2E_OUTPUT_DIR
      - junit-*.tgz
      reports:
        annotations:
        - $EXTERNAL_LINKS_PATH
      when: always
    before_script:
    - CURRENT_AGENT_VERSION=$(dda inv agent.version) || exit $?; export CURRENT_AGENT_VERSION
    - export STABLE_AGENT_VERSION_PACKAGE="7.65.0-devel-1"
-   - export PREVIOUS_AGENT_MSI_PIPELINE=58948204
?                                         -  --
+   - export PREVIOUS_AGENT_MSI_PIPELINE=59253404
?                                          +++
-   - export PREVIOUS_AGENT_OCI_PIPELINE=58948204
?                                         -  --
+   - export PREVIOUS_AGENT_OCI_PIPELINE=59253404
?                                          +++
    - mkdir -p $GOPATH/pkg/mod/cache && tar xJf modcache_e2e.tar.xz -C $GOPATH/pkg/mod/cache
      || exit 101
    - rm -f modcache_e2e.tar.xz
    - mkdir -p ~/.aws
    - "if [ -n \"$E2E_USE_AWS_PROFILE\" ]; then\n  echo Using agent-qa-ci aws profile\n\
      \  $CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_QA_E2E profile >> ~/.aws/config\
      \ || exit $?\n  # Now all `aws` commands target the agent-qa profile\n  export\
      \ AWS_PROFILE=agent-qa-ci\nelse\n  # Assume role to fetch only once credentials\
      \ and avoid rate limits\n  echo Assuming ddbuild-agent-ci role\n  roleoutput=\"\
      $(aws sts assume-role --role-arn arn:aws:iam::669783387624:role/ddbuild-agent-ci\
      \ --external-id ddbuild-agent-ci --role-session-name RoleSession)\"\n  export\
      \ AWS_ACCESS_KEY_ID=\"$(echo \"$roleoutput\" | jq -r '.Credentials.AccessKeyId')\"\
      \n  export AWS_SECRET_ACCESS_KEY=\"$(echo \"$roleoutput\" | jq -r '.Credentials.SecretAccessKey')\"\
      \n  export AWS_SESSION_TOKEN=\"$(echo \"$roleoutput\" | jq -r '.Credentials.SessionToken')\"\
      \nfi\n"
    - $CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_QA_E2E ssh_public_key_rsa > $E2E_AWS_PUBLIC_KEY_PATH
      || exit $?
    - touch $E2E_AWS_PRIVATE_KEY_PATH && chmod 600 $E2E_AWS_PRIVATE_KEY_PATH && $CI_PROJECT_DIR/tools/ci/fetch_secret.sh
      $AGENT_QA_E2E ssh_key_rsa > $E2E_AWS_PRIVATE_KEY_PATH || exit $?
    - $CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_QA_E2E ssh_public_key_rsa > $E2E_AZURE_PUBLIC_KEY_PATH
      || exit $?
    - touch $E2E_AZURE_PRIVATE_KEY_PATH && chmod 600 $E2E_AZURE_PRIVATE_KEY_PATH &&
      $CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_QA_E2E ssh_key_rsa > $E2E_AZURE_PRIVATE_KEY_PATH
      || exit $?
    - $CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_QA_E2E ssh_public_key_rsa > $E2E_GCP_PUBLIC_KEY_PATH
      || exit $?
    - touch $E2E_GCP_PRIVATE_KEY_PATH && chmod 600 $E2E_GCP_PRIVATE_KEY_PATH && $CI_PROJECT_DIR/tools/ci/fetch_secret.sh
      $AGENT_QA_E2E ssh_key_rsa > $E2E_GCP_PRIVATE_KEY_PATH || exit $?
    - pulumi login "s3://dd-pulumi-state?region=us-east-1&awssdk=v2&profile=$AWS_PROFILE"
    - ARM_CLIENT_ID=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $E2E_AZURE client_id)
      || exit $?; export ARM_CLIENT_ID
    - ARM_CLIENT_SECRET=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $E2E_AZURE token)
      || exit $?; export ARM_CLIENT_SECRET
    - ARM_TENANT_ID=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $E2E_AZURE tenant_id)
      || exit $?; export ARM_TENANT_ID
    - ARM_SUBSCRIPTION_ID=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $E2E_AZURE subscription_id)
      || exit $?; export ARM_SUBSCRIPTION_ID
    - $CI_PROJECT_DIR/tools/ci/fetch_secret.sh $E2E_GCP credentials_json > ~/gcp-credentials.json
      || exit $?
    - export GOOGLE_APPLICATION_CREDENTIALS=~/gcp-credentials.json
    - dda inv -e gitlab.generate-ci-visibility-links --output=$EXTERNAL_LINKS_PATH
    image: registry.ddbuild.io/ci/test-infra-definitions/runner$TEST_INFRA_DEFINITIONS_BUILDIMAGES_SUFFIX:$TEST_INFRA_DEFINITIONS_BUILDIMAGES
    needs:
    - go_e2e_deps
    - deploy_windows_testing-a7
    - deploy_installer_oci
    - deploy_agent_oci
    - qa_installer_script_windows
    parallel:
      matrix:
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestUpgradeMSI$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestUpgradeAgentPackage$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestDowngradeAgentPackage$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestStopExperiment$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestExperimentForNonExistingPackageFails$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestExperimentCurrentVersionFails$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestStopWithoutExperiment$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestRevertsExperimentWhenServiceDies$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestRevertsExperimentWhenTimeout$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestRunAgentMSIAfterExperiment$"
      - EXTRA_PARAMS: --run "TestAgentUpgrades/TestUpgradeWithAgentUser$"
      - EXTRA_PARAMS: --run "TestInstaller$"
      - EXTRA_PARAMS: --run "TestDotnetLibraryInstalls$"
      - EXTRA_PARAMS: --run "TestDotnetLibraryInstallsWithoutIIS$"
+     - EXTRA_PARAMS: --run "TestAgentMSIInstallsDotnetLibrary/TestInstallFromMSI$"
+     - EXTRA_PARAMS: --run "TestAgentMSIInstallsDotnetLibrary/TestMSIThenRemoteUpgrade$"
+     - EXTRA_PARAMS: --run "TestAgentMSIInstallsDotnetLibrary/TestUpgradeWithMSI$"
+     - EXTRA_PARAMS: --run "TestAgentMSIInstallsDotnetLibrary/TestMSIRollbackRemovesLibrary$"
+     - EXTRA_PARAMS: --run "TestAgentMSIInstallsDotnetLibrary/TestMSISkipRollbackIfInstalled$"
+     - EXTRA_PARAMS: --run "TestAgentMSIInstallsDotnetLibrary/TestUninstallKeepsLibrary$"
    rules:
    - if: $RUN_E2E_TESTS == "off"
      when: never
    - if: $CI_COMMIT_BRANCH =~ /^mq-working-branch-/
      when: never
    - if: $RUN_E2E_TESTS == "on"
      when: on_success
    - if: $CI_COMMIT_BRANCH == "main"
      when: on_success
    - if: $CI_COMMIT_BRANCH =~ /^[0-9]+\.[0-9]+\.x$/
      when: on_success
    - if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$/
      when: on_success
    - changes:
        compare_to: main
        paths:
        - .gitlab/e2e/e2e.yml
        - test/new-e2e/pkg/**/*
        - test/new-e2e/go.mod
        - flakes.yaml
    - changes:
        compare_to: main
        paths:
        - .gitlab/**/*
        - omnibus/config/**/*
        - pkg/fleet/**/*
        - cmd/installer/**/*
        - test/new-e2e/tests/installer/**/*
        - tasks/installer.py
      when: on_success
    - if: $CI_COMMIT_BRANCH =~ /^mq-working-branch-/
      when: never
    - allow_failure: true
      when: manual
    script:
    - dda inv -e new-e2e-tests.run --targets $TARGETS -c ddagent:imagePullRegistry=669783387624.dkr.ecr.us-east-1.amazonaws.com
      -c ddagent:imagePullUsername=AWS -c ddagent:imagePullPassword=$(aws ecr get-login-password)
      --junit-tar junit-${CI_JOB_ID}.tgz ${EXTRA_PARAMS} --test-washer --logs-folder=$E2E_OUTPUT_DIR/logs
      --logs-post-processing --logs-post-processing-test-depth=$E2E_LOGS_PROCESSING_TEST_DEPTH
    stage: e2e
    tags:
    - arch:amd64
    variables:
      E2E_AWS_PRIVATE_KEY_PATH: /tmp/agent-qa-aws-ssh-key
      E2E_AWS_PUBLIC_KEY_PATH: /tmp/agent-qa-aws-ssh-key.pub
      E2E_AZURE_PRIVATE_KEY_PATH: /tmp/agent-qa-azure-ssh-key
      E2E_AZURE_PUBLIC_KEY_PATH: /tmp/agent-qa-azure-ssh-key.pub
      E2E_COMMIT_SHA: $CI_COMMIT_SHORT_SHA
      E2E_GCP_PRIVATE_KEY_PATH: /tmp/agent-qa-gcp-ssh-key
      E2E_GCP_PUBLIC_KEY_PATH: /tmp/agent-qa-gcp-ssh-key.pub
      E2E_KEY_PAIR_NAME: datadog-agent-ci-rsa
      E2E_LOGS_PROCESSING_TEST_DEPTH: 1
      E2E_OUTPUT_DIR: $CI_PROJECT_DIR/e2e-output
      E2E_PIPELINE_ID: $CI_PIPELINE_ID
      EXTERNAL_LINKS_PATH: external_links_$CI_JOB_ID.json
      FLAKY_PATTERNS_CONFIG: $CI_PROJECT_DIR/flaky-patterns-runtime.yaml
      FLEET_INSTALL_METHOD: windows
      KUBERNETES_CPU_REQUEST: 6
      KUBERNETES_MEMORY_LIMIT: 16Gi
      KUBERNETES_MEMORY_REQUEST: 12Gi
      SHOULD_RUN_IN_FLAKES_FINDER: 'true'
      TARGETS: ./tests/installer/windows
      TEAM: fleet
```

</details>

### Changes Summary
| Removed | Modified | Added | Renamed |
| ------- | -------- | ----- | ------- |
| 0 | 1 | 0 | 0 |

:information_source: *Diff available in the [job log](https://gitlab.ddbuild.io/DataDog/datadog-agent/-/jobs/855798916).*
author_association: CONTRIBUTOR
user: login: agent-platform-auto-pr[bot]
created_at: 2025-03-18T21:51:31Z
body: ## Test changes on VM

Use this command from [test-infra-definitions](https://github.com/DataDog/test-infra-definitions) to manually test this PR changes on a VM:
```sh
dda inv aws.create-vm --pipeline-id=59385949 --os-family=ubuntu
```
Note: This applies to commit **68f3db50**
author_association: CONTRIBUTOR
user: login: clarkb7
created_at: 2025-03-19T19:25:19Z
body: rebasing to main
author_association: CONTRIBUTOR
user: login: agent-platform-auto-pr[bot]
created_at: 2025-03-19T19:52:24Z
body: 
[Fast Unit Tests Report]

On pipeline [59385949](https://gitlab.ddbuild.io/DataDog/datadog-agent/-/pipelines/59385949) ([CI Visibility](https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Apipeline%20%40ci.pipeline.name%3ADataDog%2Fdatadog-agent%20%40ci.pipeline.id%3A59385949&fromUser=false&index=cipipeline)). The following jobs did not run any unit tests:

<details>
<summary>Jobs:</summary>

  - tests_deb-arm64-py3
  - tests_deb-x64-py3
  - tests_flavor_dogstatsd_deb-x64
  - tests_flavor_heroku_deb-x64
  - tests_flavor_iot_deb-x64
  - tests_rpm-arm64-py3
  - tests_rpm-x64-py3
  - tests_windows-x64
</details>

If you modified Go files and expected unit tests to run in these jobs, please double check the job logs. If you think tests should have been executed reach out to #agent-devx-help
author_association: CONTRIBUTOR
user: login: clarkb7
created_at: 2025-03-19T21:09:51Z
body: removing e2e.yml changes for now to
- facilitate merging of other PRs that also need a new previous agent build
- allow us to create previous agent build that contains all changes and deploy it to a persistent bucket
before enabling the tests again

tests passed in previous pipelines
- https://gitlab.ddbuild.io/DataDog/datadog-agent/-/pipelines/59372087
author_association: CONTRIBUTOR
user: login: clarkb7
created_at: 2025-03-19T23:09:59Z
body: /merge
author_association: CONTRIBUTOR
user: login: dd-devflow[bot]
created_at: 2025-03-19T23:10:02Z
body: <!-- "FeedbackWorkflowId":"19596206-0554-468d-aa23-46e53e2da1c8_36" -->
[View all feedbacks in Devflow UI.](https://devflow.static-app.us1.prod.dog/logs/19596206-0554-468d-aa23-46e53e2da1c8_36)
`2025-03-19 23:10:02 UTC` :information_source: Start processing command `/merge`

---
`2025-03-19 23:10:06 UTC` :information_source: **MergeQueue:** pull request added to the queue

The expected merge time in `main` is approximately 52m (p90).

---
`2025-03-19 23:40:17 UTC` :information_source: **MergeQueue:** This merge request was merged

author_association: NONE
user: login: clarkb7
created_at: 2025-03-20T14:25:16Z
body: changelog in https://github.com/DataDog/datadog-agent/pull/35305
author_association: CONTRIBUTOR