diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml
index 2f171d5b4c2e2bc43acf821e6263910f877a6a56..0bd64368b0ecc59ce537823821263a986ab819e8 100644
--- a/.azure/pipelines/ci.yml
+++ b/.azure/pipelines/ci.yml
@@ -47,7 +47,8 @@ variables:
 - ${{ if eq(variables['System.TeamProject'], 'internal') }}:
   - group: DotNet-MSRC-Storage
   - name: _InternalRuntimeDownloadArgs
-    value: -DotNetRuntimeSourceFeed https://dotnetclimsrc.blob.core.windows.net/dotnet -DotNetRuntimeSourceFeedKey $(dotnetclimsrc-read-sas-token-base64) /p:DotNetAssetRootAccessTokenSuffix='$(dotnetclimsrc-read-sas-token-base64)'
+    value: -DotNetRuntimeSourceFeed https://dotnetclimsrc.blob.core.windows.net/dotnet -DotNetRuntimeSourceFeedKey
+           $(dotnetclimsrc-read-sas-token-base64) /p:DotNetAssetRootAccessTokenSuffix='$(dotnetclimsrc-read-sas-token-base64)'
   # The code signing doesn't use the aspnet build scripts, so the msbuild parameters have
   # to be passed directly. This is awkward, since we pass the same info above, but we have
   # to have it in two different forms
@@ -140,19 +141,21 @@ stages:
       - script: ./build.cmd
                 -ci
                 -nobl
+                -noBuildRepoTasks
                 -arch x86
                 -pack
                 -all
                 -noBuildJava
+                -noBuildNative
                 /p:OnlyPackPlatformSpecificPackages=true
                 $(_BuildArgs)
                 $(_InternalRuntimeDownloadArgs)
         displayName: Build x86
 
-      # This is in a separate build step with -forceCoreMsbuild to workaround MAX_PATH limitations - https://github.com/Microsoft/msbuild/issues/53
       - script: .\src\SiteExtensions\build.cmd
                 -ci
                 -nobl
+                -noBuildRepoTasks
                 -pack
                 -noBuildDeps
                 $(_BuildArgs)
@@ -160,12 +163,13 @@ stages:
         condition: ne(variables['Build.Reason'], 'PullRequest')
         displayName: Build SiteExtension
 
-      # This runs code-signing on all packages, zips, and jar files as defined in build/CodeSign.targets. If https://github.com/dotnet/arcade/issues/1957 is resolved,
-      # consider running code-signing inline with the other previous steps.
-      # Sign check is disabled because it is run in a separate step below, after installers are built.
+      # This runs code-signing on all packages, zips, and jar files as defined in build/CodeSign.targets. If
+      # https://github.com/dotnet/arcade/issues/1957 is resolved, consider running code-signing inline with the other
+      # previous steps. Sign check is disabled because it is run in a separate step below, after installers are built.
       - script: ./build.cmd
                 -ci
                 -nobl
+                -noBuildRepoTasks
                 -noBuild
                 -noRestore
                 -sign
@@ -177,6 +181,7 @@ stages:
       - script: ./build.cmd
                 -ci
                 -nobl
+                -noBuildRepoTasks
                 -sign
                 -buildInstallers
                 /p:DotNetSignType=$(_SignType)
@@ -492,7 +497,9 @@ stages:
       jobDisplayName: "Test: Windows Server 2016 x64"
       agentOs: Windows
       isTestingJob: true
-      buildArgs: -all -pack -test "/p:SkipHelixReadyTests=true /p:SkipIISNewHandlerTests=true /p:SkipIISTests=true /p:SkipIISExpressTests=true /p:SkipIISNewShimTests=true /p:RunTemplateTests=false" $(_InternalRuntimeDownloadArgs)
+      buildArgs: -all -pack -test /p:SkipHelixReadyTests=true /p:SkipIISNewHandlerTests=true /p:SkipIISTests=true
+                 /p:SkipIISExpressTests=true /p:SkipIISNewShimTests=true /p:RunTemplateTests=false
+                 $(_InternalRuntimeDownloadArgs)
       beforeBuild:
       - powershell: "& ./src/Servers/IIS/tools/UpdateIISExpressCertificate.ps1; & ./src/Servers/IIS/tools/update_schema.ps1"
         displayName: Setup IISExpress test certificates and schema
@@ -530,9 +537,9 @@ stages:
       steps:
       - script: ./build.cmd -ci -nobl -all -pack $(_InternalRuntimeDownloadArgs)
         displayName: Build Repo
-      - script: ./src/ProjectTemplates/build.cmd -ci -nobl -pack -NoRestore -NoBuilddeps "/p:RunTemplateTests=true"
+      - script: ./src/ProjectTemplates/build.cmd -ci -nobl -noBuildRepoTasks -pack -NoRestore -NoBuilddeps "/p:RunTemplateTests=true"
         displayName: Pack Templates
-      - script: ./src/ProjectTemplates/build.cmd -ci -nobl -test -NoRestore -NoBuild -NoBuilddeps "/p:RunTemplateTests=true"
+      - script: ./src/ProjectTemplates/build.cmd -ci -nobl -noBuildRepoTasks -test -NoRestore -NoBuild -NoBuilddeps "/p:RunTemplateTests=true"
         displayName: Test Templates
       artifacts:
       - name: Windows_Test_Templates_Dumps
@@ -632,9 +639,11 @@ stages:
       # Build the shared framework
       - script: ./build.cmd -ci -nobl -all -pack -arch x64 /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
         displayName: Build shared fx
-      - script: .\restore.cmd -ci -nobl /p:BuildInteropProjects=true
+      - script: ./build.cmd -ci -nobl -noBuildRepoTasks -restore -noBuild -projects src/Grpc/**/*.csproj
         displayName: Restore interop projects
-      - script: .\build.cmd -ci -nobl -NoRestore -test -all -projects eng\helix\helix.proj /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildInteropProjects=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
+      - script: ./build.cmd -ci -nobl -noBuildRepoTasks -noRestore -test -all -noBuildNative -projects eng\helix\helix.proj
+                /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildInteropProjects=true /p:RunTemplateTests=true
+                /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
         displayName: Run build.cmd helix target
         env:
           HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues
diff --git a/.azure/pipelines/helix-matrix.yml b/.azure/pipelines/helix-matrix.yml
index 51065fd1ae134e0bd8c64c210c1f86d7b6b8a7e9..104481013675e8f94f0c1cdfb4336eaaf84f24ba 100644
--- a/.azure/pipelines/helix-matrix.yml
+++ b/.azure/pipelines/helix-matrix.yml
@@ -7,7 +7,7 @@ schedules:
     include:
     - master
   always: true
-    
+
 variables:
 - ${{ if ne(variables['System.TeamProject'], 'internal') }}:
   - name: _UseHelixOpenQueues
@@ -15,8 +15,8 @@ variables:
 - ${{ if eq(variables['System.TeamProject'], 'internal') }}:
   - group: DotNet-HelixApi-Access
   - name: _UseHelixOpenQueues
-    value: 'false'    
-    
+    value: 'false'
+
 jobs:
 - template: jobs/default-build.yml
   parameters:
@@ -28,9 +28,11 @@ jobs:
     # Build the shared framework
     - script: ./build.cmd -ci -nobl -all -pack -arch x64 /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
       displayName: Build shared fx
-    - script: .\restore.cmd -ci /p:BuildInteropProjects=true
+    - script: ./build.cmd -ci -nobl -noBuildRepoTasks -restore -noBuild -projects src/Grpc/**/*.csproj
       displayName: Restore interop projects
-    - script: .\build.cmd -ci -nobl -NoRestore -test -all -projects eng\helix\helix.proj /p:IsHelixDaily=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildInteropProjects=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
+    - script: .\build.cmd -ci -nobl -noBuildRepoTasks -NoRestore -test -all -projects eng\helix\helix.proj
+              /p:IsHelixDaily=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildInteropProjects=true
+              /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
       displayName: Run build.cmd helix target
       env:
         HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues
@@ -39,7 +41,7 @@ jobs:
     - name: Helix_logs
       path: artifacts/log/
       publishOnError: true
-      
+
 # Helix ARM64
 - template: jobs/default-build.yml
   parameters:
@@ -50,7 +52,9 @@ jobs:
     steps:
     - script: ./restore.sh -ci -nobl
       displayName: Restore
-    - script: ./build.sh -ci --nobl --arch arm64 -test --no-build-nodejs --all -projects $(Build.SourcesDirectory)/eng/helix/helix.proj /p:IsHelixJob=true /p:IsHelixDaily=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
+    - script: ./build.sh --ci --nobl --noBuildRepoTasks --arch arm64 -test --no-build-nodejs --all --projects
+              $(Build.SourcesDirectory)/eng/helix/helix.proj /p:IsHelixJob=true /p:IsHelixDaily=true
+              /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
       displayName: Run build.sh helix arm64 target
       env:
         HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues
diff --git a/.azure/pipelines/jobs/codesign-xplat.yml b/.azure/pipelines/jobs/codesign-xplat.yml
index 2bf05f711d5c0f1ced34fbeb2a6224f35c9db31c..d65c9a59826e3be24ccc45e659817549f748e9b2 100644
--- a/.azure/pipelines/jobs/codesign-xplat.yml
+++ b/.azure/pipelines/jobs/codesign-xplat.yml
@@ -30,6 +30,7 @@ jobs:
         flattenFolders: true
     - powershell: .\eng\common\build.ps1
         -ci
+        -nobl
         -restore
         -sign
         -publish
diff --git a/.azure/pipelines/jobs/default-build.yml b/.azure/pipelines/jobs/default-build.yml
index 58619deed73e2e8200fb5faefcc24e36032bb0c1..5c6357bd4b041a130e5cbb8b4c1ab9c7d1e3edfd 100644
--- a/.azure/pipelines/jobs/default-build.yml
+++ b/.azure/pipelines/jobs/default-build.yml
@@ -104,11 +104,16 @@ jobs:
       ${{ if eq(parameters.agentOs, 'Windows') }}:
         ${{ if eq(variables['System.TeamProject'], 'public') }}:
           name: NetCorePublic-Pool
-          queue: BuildPool.Windows.10.Amd64.VS2019.Pre.Open
+          ${{ if ne(parameters.isTestingJob, true) }}:
+            # Visual Studio Build Tools
+            queue: BuildPool.Server.Amd64.VS2019.BT.Open
+          ${{ if eq(parameters.isTestingJob, true) }}:
+            # Visual Studio Enterprise - contains some stuff, like SQL Server and IIS Express, that we use for testing
+            queue: BuildPool.Server.Amd64.VS2019.Open
         ${{ if eq(variables['System.TeamProject'], 'internal') }}:
           name: NetCoreInternal-Pool
           # Visual Studio Enterprise - contains some stuff, like SQL Server and IIS Express, that we use for testing
-          queue: BuildPool.Windows.10.Amd64.VS2019.Pre
+          queue: BuildPool.Server.Amd64.VS2019
     variables:
     - AgentOsName: ${{ parameters.agentOs }}
     - ASPNETCORE_TEST_LOG_MAXPATH: "200" # Keep test log file name length low enough for artifact zipping
diff --git a/.azure/pipelines/quarantined-tests.yml b/.azure/pipelines/quarantined-tests.yml
index 9b2c9da91b43f27e2386e8ab57e0f12058cf4adb..355c94268fec0a5c74b768b15c053e84754c627a 100644
--- a/.azure/pipelines/quarantined-tests.yml
+++ b/.azure/pipelines/quarantined-tests.yml
@@ -31,11 +31,13 @@ jobs:
     timeoutInMinutes: 240
     steps:
     # Build the shared framework
-    - script: ./build.cmd -ci -all -pack -arch x64 /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log /bl:artifacts/log/helix.build.x64.binlog
+    - script: ./build.cmd -ci -nobl -all -pack -arch x64 /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
       displayName: Build shared fx
-    - script: .\restore.cmd -ci /p:BuildInteropProjects=true
+    - script: ./build.cmd -ci -nobl -noBuildRepoTasks -restore -noBuild -projects src/Grpc/**/*.csproj
       displayName: Restore interop projects
-    - script: .\build.cmd -ci -NoRestore -test -noBuildJava -all -projects eng\helix\helix.proj /p:RunQuarantinedTests=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildInteropProjects=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl
+    - script: ./build.cmd -ci -nobl -noBuildRepoTasks -noRestore -test -all -noBuildJava -noBuildNative
+              -projects eng\helix\helix.proj /p:RunQuarantinedTests=true /p:IsRequiredCheck=true /p:IsHelixJob=true
+              /p:BuildInteropProjects=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
       displayName: Run build.cmd helix target
       env:
         HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues
diff --git a/Directory.Build.props b/Directory.Build.props
index edd4ebe1aa67c447a34c96108fb52a84dbca066c..010f6b68fb77325412133b5164bbd1cd8e8a5d0e 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -180,7 +180,7 @@
     <!-- Projects which reference Microsoft.AspNetCore.Mvc.Testing should import this targets file to ensure dependency .deps.json files are copied into test output. -->
     <MvcTestingTargets>$(MSBuildThisFileDirectory)src\Mvc\Mvc.Testing\src\Microsoft.AspNetCore.Mvc.Testing.targets</MvcTestingTargets>
     <!-- IIS native projects can only be built on Windows for x86 and x64. -->
-    <BuildIisNativeProjects Condition=" $(BuildNative) AND ('$(TargetArchitecture)' == 'x86' OR '$(TargetArchitecture)' == 'x64') ">true</BuildIisNativeProjects>
+    <BuildIisNativeProjects Condition=" '$(TargetOsName)' == 'win' AND ('$(TargetArchitecture)' == 'x86' OR '$(TargetArchitecture)' == 'x64') ">true</BuildIisNativeProjects>
     <!-- This property is shared by several projects to layout the AspNetCore.App targeting pack for installers -->
     <TargetingPackLayoutRoot>$(ArtifactsObjDir)TargetingPack.Layout\$(Configuration)\</TargetingPackLayoutRoot>
     <!-- This property is shared by several projects to layout the AspNetCore.App shared framework for installers -->
diff --git a/build.ps1 b/build.ps1
index cf5b9e0655bc92911eaf6dc74c78865f1ee204f4..06fea4c768cf7c5b4e255eb780979e4dcf6c050d 100644
--- a/build.ps1
+++ b/build.ps1
@@ -146,9 +146,8 @@ param(
 
     [switch]$NoBuildRepoTasks,
 
-    # By default, Windows builds will use MSBuild.exe. Passing this will force the build to run on
-    # dotnet.exe instead, which may cause issues if you invoke build on a project unsupported by
-    # MSBuild for .NET Core
+    # Disable pre-build of C++ code in x64 (default) and x86 builds. Affects -All and -Projects handling and causes
+    # -BuildInstallers and -BuildNative to be ignored.
     [switch]$ForceCoreMsbuild,
 
     # Diagnostics
@@ -187,10 +186,6 @@ if ($DumpProcesses -or $CI) {
 }
 
 # Project selection
-if ($All) {
-    $MSBuildArguments += '/p:BuildAllProjects=true'
-}
-
 if ($Projects) {
     if (![System.IO.Path]::IsPathRooted($Projects))
     {
@@ -227,20 +222,8 @@ if ($BuildManaged -or ($All -and (-not $NoBuildManaged))) {
     }
 }
 
-if ($BuildInstallers) { $MSBuildArguments += "/p:BuildInstallers=true" }
-if ($BuildManaged) { $MSBuildArguments += "/p:BuildManaged=true" }
-if ($BuildNative) { $MSBuildArguments += "/p:BuildNative=true" }
-if ($BuildNodeJS) { $MSBuildArguments += "/p:BuildNodeJS=true" }
-if ($BuildJava) { $MSBuildArguments += "/p:BuildJava=true" }
-
 if ($NoBuildDeps) { $MSBuildArguments += "/p:BuildProjectReferences=false" }
 
-if ($NoBuildInstallers) { $MSBuildArguments += "/p:BuildInstallers=false" }
-if ($NoBuildManaged) { $MSBuildArguments += "/p:BuildManaged=false" }
-if ($NoBuildNative) { $MSBuildArguments += "/p:BuildNative=false" }
-if ($NoBuildNodeJS) { $MSBuildArguments += "/p:BuildNodeJS=false" }
-if ($NoBuildJava) { $MSBuildArguments += "/p:BuildJava=false" }
-
 $RunBuild = if ($NoBuild) { $false } else { $true }
 
 # Run restore by default unless -NoRestore is set.
@@ -276,6 +259,30 @@ if ($DotNetRuntimeSourceFeed -or $DotNetRuntimeSourceFeedKey) {
     $ToolsetBuildArguments += $runtimeFeedKeyArg
 }
 
+# Split build categories between dotnet msbuild and desktop msbuild. Use desktop msbuild as little as possible.
+[string[]]$dotnetBuildArguments = $MSBuildArguments
+if ($All) { $dotnetBuildArguments += '/p:BuildAllProjects=true'; $BuildNative = $true }
+
+if ($NoBuildInstallers) { $MSBuildArguments += "/p:BuildInstallers=false"; $BuildInstallers = $false }
+if ($BuildInstallers) { $MSBuildArguments += "/p:BuildInstallers=true" }
+if ($NoBuildNative) { $MSBuildArguments += "/p:BuildNative=false"; $BuildNative = $false }
+if ($BuildNative) { $MSBuildArguments += "/p:BuildNative=true"}
+
+if ($NoBuildJava) { $dotnetBuildArguments += "/p:BuildJava=false"; $BuildJava = $false }
+if ($BuildJava) { $dotnetBuildArguments += "/p:BuildJava=true" }
+if ($NoBuildManaged) { $dotnetBuildArguments += "/p:BuildManaged=false"; $BuildManaged = $false }
+if ($BuildManaged) { $dotnetBuildArguments += "/p:BuildManaged=true" }
+if ($NoBuildNodeJS) { $dotnetBuildArguments += "/p:BuildNodeJS=false"; $BuildNodeJS = $false }
+if ($BuildNodeJS) { $dotnetBuildArguments += "/p:BuildNodeJS=true" }
+
+# Don't bother with two builds if just one will build everything. Ignore super-weird cases like
+# "-Projects ... -NoBuildJava -NoBuildManaged -NoBuildNodeJS".
+$ForceCoreMsbuild = $ForceCoreMsbuild -or -not ($BuildInstallers -or $BuildNative) -or `
+    $Architecture.StartsWith("arm", [System.StringComparison]::OrdinalIgnoreCase)
+$performDotnetBuild = $ForceCoreMsbuild -or $BuildJava -or $BuildManaged -or $BuildNodeJS -or `
+    ($All -and -not ($NoBuildJava -and $NoBuildManaged -and $NoBuildNodeJS)) -or `
+    ($Projects -and -not ($BuildInstallers -or $BuildNative))
+
 $foundJdk = $false
 $javac = Get-Command javac -ErrorAction Ignore -CommandType Application
 $localJdkPath = "$PSScriptRoot\.tools\jdk\win-x64\"
@@ -343,9 +350,8 @@ $env:MSBUILDDISABLENODEREUSE=1
 # Fixing this is tracked by https://github.com/dotnet/aspnetcore-internal/issues/601
 $warnAsError = $false
 
-if ($ForceCoreMsbuild) {
-    $msbuildEngine = 'dotnet'
-}
+# Use `dotnet msbuild` by default
+$msbuildEngine = 'dotnet'
 
 # Ensure passing neither -bl nor -nobl on CI avoids errors in tools.ps1. This is needed because both parameters are
 # $false by default i.e. they always exist. (We currently avoid binary logs but that is made visible in the YAML.)
@@ -367,7 +373,16 @@ Remove-Item variable:global:_MSBuildExe -ea Ignore
 if ($BinaryLog) {
     $bl = GetMSBuildBinaryLogCommandLineArgument($MSBuildArguments)
     if (-not $bl) {
-        $MSBuildArguments += "/bl:" + (Join-Path $LogDir "Build.binlog")
+        $dotnetBuildArguments += "/bl:" + (Join-Path $LogDir "Build.binlog")
+        $MSBuildArguments += "/bl:" + (Join-Path $LogDir "Build.native.binlog")
+        $ToolsetBuildArguments += "/bl:" + (Join-Path $LogDir "Build.repotasks.binlog")
+    } else {
+        # Use a different binary log path when running desktop msbuild if doing both builds.
+        if (-not $ForceCoreMsbuild -and $performDotnetBuild) {
+            $MSBuildArguments += [System.IO.Path]::ChangeExtension($bl, "native.binlog")
+        }
+
+        $ToolsetBuildArguments += [System.IO.Path]::ChangeExtension($bl, "repotasks.binlog")
     }
 } elseif ($CI) {
     # Ensure the artifacts/log directory isn't empty to avoid warnings.
@@ -394,6 +409,8 @@ try {
     }
 
     if (-not $NoBuildRepoTasks) {
+        Write-Host
+
         MSBuild $toolsetBuildProj `
             /p:RepoRoot=$RepoRoot `
             /p:Projects=$EngRoot\tools\RepoTasks\RepoTasks.csproj `
@@ -404,9 +421,21 @@ try {
             @ToolsetBuildArguments
     }
 
-    MSBuild $toolsetBuildProj `
-        /p:RepoRoot=$RepoRoot `
-        @MSBuildArguments
+    if (-not $ForceCoreMsbuild) {
+        Write-Host
+        Remove-Item variable:global:_BuildTool -ErrorAction Ignore
+        $msbuildEngine = 'vs'
+
+        MSBuild $toolsetBuildProj /p:RepoRoot=$RepoRoot @MSBuildArguments
+    }
+
+    if ($performDotnetBuild) {
+        Write-Host
+        Remove-Item variable:global:_BuildTool -ErrorAction Ignore
+        $msbuildEngine = 'dotnet'
+
+        MSBuild $toolsetBuildProj /p:RepoRoot=$RepoRoot @dotnetBuildArguments
+    }
 }
 catch {
     Write-Host $_.ScriptStackTrace
diff --git a/eng/AfterSolutionBuild.targets b/eng/AfterSolutionBuild.targets
index d3697c09ee1d3a32099583a41fb278b2b0c78596..4a3cb4ba2adf867e73bf56fdd0715203c506faaf 100644
--- a/eng/AfterSolutionBuild.targets
+++ b/eng/AfterSolutionBuild.targets
@@ -5,7 +5,8 @@
   <Import Project="SharedFramework.Local.props" />
 
   <!-- This is temporary until we can use FrameworkReference to build our own packages. -->
-  <Target Name="RemoveSharedFrameworkOnlyRefsFromNuspec" AfterTargets="Pack">
+  <Target Name="RemoveSharedFrameworkOnlyRefsFromNuspec" AfterTargets="Pack"
+      Condition=" '$(MSBuildRuntimeType)' == 'core' ">
     <ItemGroup>
       <_BuildOutput Include="$(ArtifactsShippingPackagesDir)*.nupkg"
                     Exclude="$(ArtifactsShippingPackagesDir)*.symbols.nupkg" />
diff --git a/eng/Build.props b/eng/Build.props
index 2af7e7dedf394ac70fb117ccef24f7341ff660b7..2c1098a3900b0f028f1f42389f8c814a7f0ec465 100644
--- a/eng/Build.props
+++ b/eng/Build.props
@@ -84,14 +84,12 @@
                         Include="$(RepoRoot)src\Installers\Rpm\**\*.*proj" />
       </ItemGroup>
 
-      <ItemGroup>
-        <NativeProjects Condition=" '$(TargetOsName)' == 'win' AND ('$(TargetArchitecture)' == 'x86' OR '$(TargetArchitecture)' == 'x64') "
-                        Include="$(RepoRoot)src\**\*.vcxproj" Exclude="@(ProjectToExclude)">
-          <!-- Required to prevent triggering double-builds. See src\Servers\IIS\ResolveIisReferences.targets for details. -->
-          <AdditionalProperties Condition="'$(TargetArchitecture)' == 'x64'">Platform=x64</AdditionalProperties>
-          <AdditionalProperties Condition="'$(TargetArchitecture)' == 'x86'">Platform=Win32</AdditionalProperties>
-        </NativeProjects>
+      <ItemGroup Condition=" '$(TargetOsName)' == 'win' AND ('$(TargetArchitecture)' == 'x86' OR '$(TargetArchitecture)' == 'x64') ">
+        <NativeProjects Include="$(RepoRoot)src\**\*.vcxproj" Exclude="@(ProjectToExclude)" AdditionalProperties="Platform=x64" />
+        <NativeProjects Include="$(RepoRoot)src\**\*.vcxproj" Exclude="@(ProjectToExclude)" AdditionalProperties="Platform=Win32" />
+      </ItemGroup>
 
+      <ItemGroup>
         <ProjectToBuild Condition=" $(BuildNative) " Include="@(NativeProjects)" Exclude="@(ProjectToExclude)" />
         <ProjectToExclude Condition=" !$(BuildNative) " Include="@(NativeProjects)" />
 
diff --git a/eng/scripts/GenerateProjectList.ps1 b/eng/scripts/GenerateProjectList.ps1
index 47449b3d7ae86b54939980801e83a2b20af5619d..5135db533b770673081040c2b7bbd4ad1a49a8ab 100644
--- a/eng/scripts/GenerateProjectList.ps1
+++ b/eng/scripts/GenerateProjectList.ps1
@@ -1,10 +1,17 @@
 param(
     [switch]$ci
 )
-$ErrorActionPreference = 'stop'
 
+$ErrorActionPreference = 'stop'
+$excludeCIBinarylog = $true
+$msbuildEngine = 'dotnet'
 $repoRoot = Resolve-Path "$PSScriptRoot/../.."
 
-& "$repoRoot\eng\common\msbuild.ps1" -ci:$ci "$repoRoot/eng/CodeGen.proj" `
-    /t:GenerateProjectList `
-    /bl:artifacts/log/genprojlist.binlog
+try {
+  & "$repoRoot\eng\common\msbuild.ps1" -ci:$ci "$repoRoot/eng/CodeGen.proj" /t:GenerateProjectList
+} finally {
+  Remove-Item variable:global:_BuildTool -ErrorAction Ignore
+  Remove-Item variable:global:_DotNetInstallDir -ErrorAction Ignore
+  Remove-Item variable:global:_ToolsetBuildProj -ErrorAction Ignore
+  Remove-Item variable:global:_MSBuildExe -ErrorAction Ignore
+}
diff --git a/eng/scripts/GenerateReferenceAssemblies.ps1 b/eng/scripts/GenerateReferenceAssemblies.ps1
index fa58025c34ed023ed560583c14be308d62638f2d..56c1101fb325de2b484f31a28bf710513b2f753a 100644
--- a/eng/scripts/GenerateReferenceAssemblies.ps1
+++ b/eng/scripts/GenerateReferenceAssemblies.ps1
@@ -1,10 +1,25 @@
 param(
     [switch]$ci
 )
-$ErrorActionPreference = 'stop'
 
+$ErrorActionPreference = 'stop'
 $repoRoot = Resolve-Path "$PSScriptRoot/../.."
 
-& "$repoRoot\eng\common\msbuild.ps1" -ci:$ci "$repoRoot/eng/CodeGen.proj" `
-    /t:GenerateReferenceSources `
-    /bl:artifacts/log/genrefassemblies.binlog
+try {
+  # eng/common/msbuild.ps1 builds the Debug configuration unless there's a $Configuration variable. Match that here.
+  & "$repoRoot\build.ps1"  -ci:$ci -nobl -noBuildRepoTasks -noRestore -buildNative -configuration Debug
+
+  Remove-Item variable:global:_BuildTool -ErrorAction Ignore
+  Remove-Item variable:global:_DotNetInstallDir -ErrorAction Ignore
+  Remove-Item variable:global:_ToolsetBuildProj -ErrorAction Ignore
+  Remove-Item variable:global:_MSBuildExe -ErrorAction Ignore
+
+  $excludeCIBinarylog = $true
+  $msbuildEngine = 'dotnet'
+  & "$repoRoot\eng\common\msbuild.ps1" -ci:$ci "$repoRoot/eng/CodeGen.proj" /t:GenerateReferenceSources
+} finally {
+  Remove-Item variable:global:_BuildTool -ErrorAction Ignore
+  Remove-Item variable:global:_DotNetInstallDir -ErrorAction Ignore
+  Remove-Item variable:global:_ToolsetBuildProj -ErrorAction Ignore
+  Remove-Item variable:global:_MSBuildExe -ErrorAction Ignore
+}
diff --git a/eng/targets/ResolveIisReferences.targets b/eng/targets/ResolveIisReferences.targets
index a13559c7625e958ec19c1b005caf12a5f5bb02b9..5e4632a7495a26a97afc0c8d668eb0a88e709091 100644
--- a/eng/targets/ResolveIisReferences.targets
+++ b/eng/targets/ResolveIisReferences.targets
@@ -30,11 +30,4 @@ with the right MSBuild incantations to get output copied to the right place.
     </ProjectReference>
     <NativeProjectReference Remove="@(NativeProjectReference)" />
   </ItemGroup>
-
-  <Target Name="_WarnAboutUnbuiltNativeDependencies"
-          BeforeTargets="Build"
-          Condition=" @(NativeProjectReference->Count()) != 0 AND !$(BuildNative) ">
-    <Warning Text="This project has native dependencies which were not built. Without this, tests may not function correctly. Run `build.cmd -native` to build native projects. Run `build.cmd -managed -native` to build both C# and C++." />
-  </Target>
-
 </Project>
diff --git a/global.json b/global.json
index d16af0f0ea2dbc1944cb8d6d54de7c4288db0a50..4d4e54621db4a0e67b299563a9fcd20b18eb655c 100644
--- a/global.json
+++ b/global.json
@@ -22,7 +22,7 @@
     "Git": "2.22.0",
     "jdk": "11.0.3",
     "vs": {
-      "version": "16.6",
+      "version": "16.5",
       "components": [
         "Microsoft.VisualStudio.Component.VC.ATL",
         "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
diff --git a/src/Framework/src/Microsoft.AspNetCore.App.Runtime.csproj b/src/Framework/src/Microsoft.AspNetCore.App.Runtime.csproj
index adcdc8fbeea6733102b2e2ae545c56bbc8b8493d..091b78b776e07e6da5c41786d5b607e54bd705d4 100644
--- a/src/Framework/src/Microsoft.AspNetCore.App.Runtime.csproj
+++ b/src/Framework/src/Microsoft.AspNetCore.App.Runtime.csproj
@@ -117,6 +117,9 @@ This package is an internal implementation of the .NET Core SDK and is not meant
 
     <FrameworkListFileName>RuntimeList.xml</FrameworkListFileName>
     <FrameworkListOutputPath>$(ArtifactsObjDir)$(FrameworkListFileName)</FrameworkListOutputPath>
+
+    <NativePlatform>$(TargetArchitecture)</NativePlatform>
+    <NativePlatform Condition=" '$(NativePlatform)' == 'x86' ">Win32</NativePlatform>
   </PropertyGroup>
 
   <ItemGroup>
@@ -127,10 +130,9 @@ This package is an internal implementation of the .NET Core SDK and is not meant
 
     <Reference Include="microsoft.netcore.app.runtime.$(RuntimeIdentifier)" ExcludeAssets="All" PrivateAssets="All" />
 
-    <ProjectReference Condition=" '$(BuildIisNativeProjects)' == 'true' "
+    <ProjectReference Condition=" '$(BuildIisNativeProjects)' == 'true' AND $(BuildNative) "
         Include="$(RepoRoot)src\Servers\IIS\AspNetCoreModuleV2\InProcessRequestHandler\InProcessRequestHandler.vcxproj">
-      <SetPlatform>Platform=$(TargetArchitecture)</SetPlatform>
-      <SetPlatform Condition="'$(TargetArchitecture)' == 'x86'">Platform=Win32</SetPlatform>
+      <SetPlatform>Platform=$(NativePlatform)</SetPlatform>
       <!-- Custom attribute used to distinguish managed from native references. -->
       <IsNativeImage>true</IsNativeImage>
       <!-- A custom item group to be used in targets later.  -->
@@ -138,6 +140,11 @@ This package is an internal implementation of the .NET Core SDK and is not meant
       <!-- C++ projects don't invoke GetTargetPath by default when building. -->
       <Targets>Build;GetTargetPath</Targets>
     </ProjectReference>
+
+    <_ResolvedNativeProjectReferencePaths Condition=" '$(BuildIisNativeProjects)' == 'true' AND !$(BuildNative) "
+        Include="$(ArtifactsBinDir)InProcessRequestHandler\$(NativePlatform)\$(Configuration)\aspnetcorev2_inprocess.dll">
+      <IsNativeImage>true</IsNativeImage>
+    </_ResolvedNativeProjectReferencePaths>
   </ItemGroup>
 
   <ItemGroup>
@@ -216,7 +223,8 @@ This package is an internal implementation of the .NET Core SDK and is not meant
 
   <Target Name="_WarnAboutUnbuiltNativeDependencies"
           BeforeTargets="Build"
-          Condition=" '$(BuildIisNativeProjects)' == 'true' AND !$(BuildNative) ">
+          Condition=" '$(BuildIisNativeProjects)' == 'true' AND !$(BuildNative) AND
+              !EXISTS('$(ArtifactsBinDir)InProcessRequestHandler\$(NativePlatform)\$(Configuration)\aspnetcorev2_inprocess.dll') ">
     <Warning Text="This project has native dependencies which were not built. Without this, tests may not function correctly. Run `build.cmd -BuildNative -BuildManaged` to build both C# and C++." />
   </Target>
 
diff --git a/src/SiteExtensions/build.cmd b/src/SiteExtensions/build.cmd
index 76ca111525b3fc618d45ef31b36b54ea5a4f4dcd..8f9d90787927741bdd87066021bee65a3d7f34f3 100644
--- a/src/SiteExtensions/build.cmd
+++ b/src/SiteExtensions/build.cmd
@@ -18,21 +18,21 @@ IF %ERRORLEVEL% NEQ 0 (
 ECHO Building x64 LoggingBranch
 REM /p:DisableTransitiveFrameworkReferences=true is needed to prevent SDK from picking up transitive references to
 REM Microsoft.AspNetCore.App as framework references https://github.com/dotnet/sdk/pull/3221
-CALL "%RepoRoot%\build.cmd" -forceCoreMsbuild -arch x64 -projects "%~dp0LoggingBranch\LB.csproj" ^
+CALL "%RepoRoot%\build.cmd" -arch x64 -projects "%~dp0LoggingBranch\LB.csproj" ^
     /p:DisableTransitiveFrameworkReferences=true /bl:artifacts/log/SiteExtensions-LoggingBranch-x64.binlog %*
 IF %ERRORLEVEL% NEQ 0 (
    EXIT /b %ErrorLevel%
 )
 
 ECHO Building x86 LoggingBranch
-CALL "%RepoRoot%\build.cmd" -forceCoreMsbuild -arch x86 -projects "%~dp0LoggingBranch\LB.csproj" ^
+CALL "%RepoRoot%\build.cmd" -arch x86 -projects "%~dp0LoggingBranch\LB.csproj" ^
     /p:DisableTransitiveFrameworkReferences=true /bl:artifacts/log/SiteExtensions-LoggingBranch-x86.binlog %*
 IF %ERRORLEVEL% NEQ 0 (
    EXIT /b %ErrorLevel%
 )
 
 ECHO Building Microsoft.AspNetCore.AzureAppServices.SiteExtension
-CALL "%RepoRoot%\build.cmd" -forceCoreMsbuild -projects ^
+CALL "%RepoRoot%\build.cmd" -projects ^
     "%~dp0LoggingAggregate\src\Microsoft.AspNetCore.AzureAppServices.SiteExtension\Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj" ^
     /bl:artifacts/log/SiteExtensions-LoggingAggregate.binlog %*
 IF %ERRORLEVEL% NEQ 0 (