From d180bc6df297422f189ffd08a0dd558bfbeba1ca Mon Sep 17 00:00:00 2001
From: Sean Hall <r.sean.hall@gmail.com>
Date: Sun, 15 Jan 2023 19:17:29 -0600
Subject: Add netfx:DotNetCoreSdkFeatureBandSearch.

7058
---
 src/api/wix/WixToolset.Data/WixVersionLabel.cs     |  43 ---
 src/ext/NetFx/be/detectnetcore.cpp                 |  91 +-----
 src/ext/NetFx/be/detectnetcoresdk.cpp              |  99 +------
 src/ext/NetFx/be/detectnetcoresdkfeatureband.cpp   |  55 ++++
 src/ext/NetFx/be/detectnetcoresdkfeatureband.h     |   9 +
 src/ext/NetFx/be/netfxbe.vcxproj                   |   4 +
 src/ext/NetFx/be/netfxsearch.cpp                   |  32 +-
 src/ext/NetFx/be/netfxsearch.h                     |  10 +-
 src/ext/NetFx/be/precomp.h                         |   2 +
 src/ext/NetFx/be/runnetcoresearch.cpp              | 102 +++++++
 src/ext/NetFx/be/runnetcoresearch.h                |   9 +
 src/ext/NetFx/netcoresearch/netcoresearch.cpp      | 327 ++++++++++++---------
 .../UsingDotNetCorePackages/BundleLatest.wxs       |   2 +
 .../UsingDotNetCorePackages/BundleLatest_x64.wxs   |   1 +
 .../UsingDotNetCorePackages/NetCore3.1.12_x64.wxs  |  17 +-
 .../UsingDotNetCorePackages/NetCore3.1.12_x86.wxs  |  17 +-
 .../UsingDotNetCorePackages/NetCore3_Platform.wxi  |   5 +-
 src/ext/NetFx/wixext/NetFxCompiler.cs              | 190 +++++++++++-
 .../NetFxNetCoreSearchSdkFeatureBandSymbol.cs      |  71 +++++
 .../wixext/Symbols/NetFxNetCoreSearchSdkSymbol.cs  |  25 +-
 .../NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs |   7 +-
 .../TestBA/TestBAWixlib/NetCore6.0.9_x86.wxs       |  21 +-
 .../TestBA/TestBAWixlib/NetCore6_Platform.wxi      |   7 +-
 .../TestBA/TestBAWixlib_x64/NetCore6.0.9_x64.wxs   |  21 +-
 .../TestData/TestBA/TestBAWixlib_x64/WixBA_x64.wxs |   6 +-
 25 files changed, 745 insertions(+), 428 deletions(-)
 delete mode 100644 src/api/wix/WixToolset.Data/WixVersionLabel.cs
 create mode 100644 src/ext/NetFx/be/detectnetcoresdkfeatureband.cpp
 create mode 100644 src/ext/NetFx/be/detectnetcoresdkfeatureband.h
 create mode 100644 src/ext/NetFx/be/runnetcoresearch.cpp
 create mode 100644 src/ext/NetFx/be/runnetcoresearch.h
 create mode 100644 src/ext/NetFx/wixext/Symbols/NetFxNetCoreSearchSdkFeatureBandSymbol.cs

(limited to 'src')

diff --git a/src/api/wix/WixToolset.Data/WixVersionLabel.cs b/src/api/wix/WixToolset.Data/WixVersionLabel.cs
deleted file mode 100644
index c4227c49..00000000
--- a/src/api/wix/WixToolset.Data/WixVersionLabel.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
-
-namespace WixToolset.Data
-{
-    using System;
-
-    /// <summary>
-    /// Label in a <c>WixVersion</c>.
-    /// </summary>
-    [CLSCompliant(false)]
-    public class WixVersionLabel
-    {
-        /// <summary>
-        /// Creates a string only version label.
-        /// </summary>
-        /// <param name="label">String value for version label.</param>
-        public WixVersionLabel(string label)
-        {
-            this.Label = label;
-        }
-
-        /// <summary>
-        /// Creates a string version label with numeric value.
-        /// </summary>
-        /// <param name="label">String value for version label.</param>
-        /// <param name="numeric">Numeric value for the version label.</param>
-        public WixVersionLabel(string label, uint? numeric)
-        {
-            this.Label = label;
-            this.Numeric = numeric;
-        }
-
-        /// <summary>
-        /// Gets the string label value.
-        /// </summary>
-        public string Label { get; set; }
-
-        /// <summary>
-        /// Gets the optional numeric label value.
-        /// </summary>
-        public uint? Numeric { get; set; }
-    }
-}
diff --git a/src/ext/NetFx/be/detectnetcore.cpp b/src/ext/NetFx/be/detectnetcore.cpp
index aeb04203..3ed26549 100644
--- a/src/ext/NetFx/be/detectnetcore.cpp
+++ b/src/ext/NetFx/be/detectnetcore.cpp
@@ -12,18 +12,7 @@ HRESULT DetectNetCore(
 {
     HRESULT hr = S_OK;
     LPCWSTR wzRuntimeType = NULL;
-    LPCWSTR wzPlatformName = NULL;
-    LPWSTR sczExePath = NULL;
-    LPWSTR sczCommandLine = NULL;
-    HANDLE hProcess = NULL;
-    HANDLE hStdOutErr = INVALID_HANDLE_VALUE;
-    BYTE* rgbOutput = NULL;
-    DWORD cbOutput = 0;
-    DWORD cbTotalRead = 0;
-    DWORD cbRead = 0;
-    DWORD dwExitCode = 0;
-
-    ReleaseNullStr(*psczLatestVersion);
+    LPWSTR sczArguments = NULL;
 
     switch (runtimeType)
     {
@@ -41,80 +30,14 @@ HRESULT DetectNetCore(
         break;
     }
 
-    switch (platform)
-    {
-    case NETFX_NET_CORE_PLATFORM_ARM64:
-        wzPlatformName = L"arm64";
-        break;
-    case NETFX_NET_CORE_PLATFORM_X64:
-        wzPlatformName = L"x64";
-        break;
-    case NETFX_NET_CORE_PLATFORM_X86:
-        wzPlatformName = L"x86";
-        break;
-    default:
-        BextExitWithRootFailure(hr, E_INVALIDARG, "Unknown platform: %u", platform);
-        break;
-    }
-
-    hr = StrAllocFormatted(&sczExePath, L"%ls%ls\\netcoresearch.exe", wzBaseDirectory, wzPlatformName);
-    BextExitOnFailure(hr, "Failed to build netcoresearch.exe path.");
-
-    hr = StrAllocFormatted(&sczCommandLine, L"\"%ls\" runtime %ls %ls", sczExePath, wzMajorVersion, wzRuntimeType);
-    BextExitOnFailure(hr, "Failed to build netcoresearch.exe command line.");
-
-    hr = ProcExecute(sczExePath, sczCommandLine, &hProcess, NULL, &hStdOutErr);
-    if (HRESULT_FROM_WIN32(ERROR_EXE_MACHINE_TYPE_MISMATCH) == hr)
-    {
-        ExitFunction1(hr = S_FALSE);
-    }
-    BextExitOnFailure(hr, "Failed to run: %ls", sczCommandLine);
-
-    cbOutput = 64;
+    hr = StrAllocFormatted(&sczArguments, L"runtime %ls %ls", wzMajorVersion, wzRuntimeType);
+    BextExitOnFailure(hr, "Failed to build runtime netcoresearch.exe arguments.");
 
-    rgbOutput = reinterpret_cast<BYTE*>(MemAlloc(cbOutput, TRUE));
-    BextExitOnNull(rgbOutput, hr, E_OUTOFMEMORY, "Failed to alloc output string.");
-
-    while (::ReadFile(hStdOutErr, rgbOutput + cbTotalRead, cbOutput - cbTotalRead, &cbRead, NULL))
-    {
-        cbTotalRead += cbRead;
-
-        if (cbTotalRead == cbOutput)
-        {
-            cbOutput *= 2;
-
-            LPVOID pvNew = MemReAlloc(rgbOutput, cbOutput, TRUE);
-            BextExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to realloc output string.");
-
-            rgbOutput = reinterpret_cast<BYTE*>(pvNew);
-        }
-    }
-
-    if (ERROR_BROKEN_PIPE != ::GetLastError())
-    {
-        BextExitWithLastError(hr, "Failed to read netcoresearch.exe output.");
-    }
-
-    hr = ProcWaitForCompletion(hProcess, INFINITE, &dwExitCode);
-    BextExitOnFailure(hr, "Failed to wait for netcoresearch.exe to exit.");
-
-    if (0 != dwExitCode)
-    {
-        BextExitWithRootFailure(hr, E_UNEXPECTED, "netcoresearch.exe failed with exit code: 0x%x\r\nOutput:\r\n%hs", dwExitCode, rgbOutput);
-    }
-
-    if (*rgbOutput)
-    {
-        hr = StrAllocStringAnsi(psczLatestVersion, reinterpret_cast<LPSTR>(rgbOutput), 0, CP_UTF8);
-        BextExitOnFailure(hr, "Failed to widen output string: %hs", rgbOutput);
-    }
+    hr = RunNetCoreSearch(platform, wzBaseDirectory, sczArguments, psczLatestVersion);
+    BextExitOnFailure(hr, "Failed to run netcoresearch.exe for runtime.");
 
 LExit:
-    ReleaseFileHandle(hStdOutErr);
-    ReleaseHandle(hProcess);
-    ReleaseMem(rgbOutput);
-    ReleaseStr(sczCommandLine);
-    ReleaseStr(sczExePath);
+    ReleaseStr(sczArguments);
 
     return hr;
 }
@@ -136,5 +59,7 @@ HRESULT NetfxPerformDetectNetCore(
     BextExitOnFailure(hr, "Failed to set variable '%ls' to '%ls'", wzVariable, sczLatestVersion);
 
 LExit:
+    ReleaseStr(sczLatestVersion);
+
     return hr;
 }
diff --git a/src/ext/NetFx/be/detectnetcoresdk.cpp b/src/ext/NetFx/be/detectnetcoresdk.cpp
index 08b18334..332d8712 100644
--- a/src/ext/NetFx/be/detectnetcoresdk.cpp
+++ b/src/ext/NetFx/be/detectnetcoresdk.cpp
@@ -4,99 +4,22 @@
 
 HRESULT DetectNetCoreSdk(
     __in NETFX_NET_CORE_PLATFORM platform,
-    __in LPCWSTR wzVersion,
+    __in LPCWSTR wzMajorVersion,
     __in LPCWSTR wzBaseDirectory,
     __inout LPWSTR* psczLatestVersion
     )
 {
     HRESULT hr = S_OK;
-    LPCWSTR wzPlatformName = NULL;
-    LPWSTR sczExePath = NULL;
-    LPWSTR sczCommandLine = NULL;
-    HANDLE hProcess = NULL;
-    HANDLE hStdOutErr = INVALID_HANDLE_VALUE;
-    BYTE* rgbOutput = NULL;
-    DWORD cbOutput = 0;
-    DWORD cbTotalRead = 0;
-    DWORD cbRead = 0;
-    DWORD dwExitCode = 0;
+    LPWSTR sczArguments = NULL;
 
-    ReleaseNullStr(*psczLatestVersion);
+    hr = StrAllocFormatted(&sczArguments, L"sdk %ls", wzMajorVersion);
+    BextExitOnFailure(hr, "Failed to build sdk netcoresearch.exe arguments.");
 
-    switch (platform)
-    {
-    case NETFX_NET_CORE_PLATFORM_ARM64:
-        wzPlatformName = L"arm64";
-        break;
-    case NETFX_NET_CORE_PLATFORM_X64:
-        wzPlatformName = L"x64";
-        break;
-    case NETFX_NET_CORE_PLATFORM_X86:
-        wzPlatformName = L"x86";
-        break;
-    default:
-        BextExitWithRootFailure(hr, E_INVALIDARG, "Unknown platform: %u", platform);
-        break;
-    }
-
-    hr = StrAllocFormatted(&sczExePath, L"%ls%ls\\netcoresearch.exe", wzBaseDirectory, wzPlatformName);
-    BextExitOnFailure(hr, "Failed to build netcoresearch.exe path.");
-
-    hr = StrAllocFormatted(&sczCommandLine, L"\"%ls\" sdk %ls", sczExePath, wzVersion);
-    BextExitOnFailure(hr, "Failed to build netcoresearch.exe command line.");
-
-    hr = ProcExecute(sczExePath, sczCommandLine, &hProcess, NULL, &hStdOutErr);
-    if (HRESULT_FROM_WIN32(ERROR_EXE_MACHINE_TYPE_MISMATCH) == hr)
-    {
-        ExitFunction1(hr = S_FALSE);
-    }
-    BextExitOnFailure(hr, "Failed to run: %ls", sczCommandLine);
-
-    cbOutput = 64;
-
-    rgbOutput = static_cast<BYTE*>(MemAlloc(cbOutput, TRUE));
-    BextExitOnNull(rgbOutput, hr, E_OUTOFMEMORY, "Failed to alloc output string.");
-
-    while (::ReadFile(hStdOutErr, rgbOutput + cbTotalRead, cbOutput - cbTotalRead, &cbRead, NULL))
-    {
-        cbTotalRead += cbRead;
-
-        if (cbTotalRead == cbOutput)
-        {
-            cbOutput *= 2;
-
-            const LPVOID pvNew = MemReAlloc(rgbOutput, cbOutput, TRUE);
-            BextExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to realloc output string.");
-
-            rgbOutput = static_cast<BYTE*>(pvNew);
-        }
-    }
-
-    if (ERROR_BROKEN_PIPE != ::GetLastError())
-    {
-        BextExitWithLastError(hr, "Failed to read netcoresearch.exe output.");
-    }
-
-    hr = ProcWaitForCompletion(hProcess, INFINITE, &dwExitCode);
-    BextExitOnFailure(hr, "Failed to wait for netcoresearch.exe to exit.");
-
-    if (0 != dwExitCode)
-    {
-        BextExitWithRootFailure(hr, E_UNEXPECTED, "netcoresearch.exe failed with exit code: 0x%x\r\nOutput:\r\n%hs", dwExitCode, rgbOutput);
-    }
-
-    if (*rgbOutput)
-    {
-        hr = StrAllocStringAnsi(psczLatestVersion, reinterpret_cast<LPSTR>(rgbOutput), 0, CP_UTF8);
-        BextExitOnFailure(hr, "Failed to widen output string: %hs", rgbOutput);
-    }
+    hr = RunNetCoreSearch(platform, wzBaseDirectory, sczArguments, psczLatestVersion);
+    BextExitOnFailure(hr, "Failed to run netcoresearch.exe for sdk.");
 
 LExit:
-    ReleaseFileHandle(hStdOutErr);
-    ReleaseHandle(hProcess);
-    ReleaseMem(rgbOutput);
-    ReleaseStr(sczCommandLine);
-    ReleaseStr(sczExePath);
+    ReleaseStr(sczArguments);
 
     return hr;
 }
@@ -109,14 +32,16 @@ HRESULT NetfxPerformDetectNetCoreSdk(
     )
 {
     HRESULT hr = S_OK;
-    LPWSTR sczLatestVersion = nullptr;
-    const auto& searchParams = pSearch->NetCoreSdkSearch;
-    hr = DetectNetCoreSdk(searchParams.platform, searchParams.sczVersion, wzBaseDirectory, &sczLatestVersion);
+    LPWSTR sczLatestVersion = NULL;
+
+    hr = DetectNetCoreSdk(pSearch->NetCoreSdkSearch.platform, pSearch->NetCoreSdkSearch.sczMajorVersion, wzBaseDirectory, &sczLatestVersion);
     BextExitOnFailure(hr, "DetectNetCoreSdk failed.");
 
     hr = pEngine->SetVariableVersion(wzVariable, sczLatestVersion);
     BextExitOnFailure(hr, "Failed to set variable '%ls' to '%ls'", wzVariable, sczLatestVersion);
 
 LExit:
+    ReleaseStr(sczLatestVersion);
+
     return hr;
 }
diff --git a/src/ext/NetFx/be/detectnetcoresdkfeatureband.cpp b/src/ext/NetFx/be/detectnetcoresdkfeatureband.cpp
new file mode 100644
index 00000000..d48c7a85
--- /dev/null
+++ b/src/ext/NetFx/be/detectnetcoresdkfeatureband.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+#include "precomp.h"
+
+HRESULT DetectNetCoreSdkFeatureBand(
+    __in NETFX_NET_CORE_PLATFORM platform,
+    __in LPCWSTR wzMajorVersion,
+    __in LPCWSTR wzMinorVersion,
+    __in LPCWSTR wzPatchVersion,
+    __in LPCWSTR wzBaseDirectory,
+    __inout LPWSTR* psczLatestVersion
+    )
+{
+    HRESULT hr = S_OK;
+    LPWSTR sczArguments = NULL;
+
+    hr = StrAllocFormatted(&sczArguments, L"sdkfeatureband %ls %ls %ls", wzMajorVersion, wzMinorVersion, wzPatchVersion);
+    BextExitOnFailure(hr, "Failed to build sdkfeatureband netcoresearch.exe arguments.");
+
+    hr = RunNetCoreSearch(platform, wzBaseDirectory, sczArguments, psczLatestVersion);
+    BextExitOnFailure(hr, "Failed to run netcoresearch.exe for sdkfeatureband.");
+
+LExit:
+    ReleaseStr(sczArguments);
+
+    return hr;
+}
+
+HRESULT NetfxPerformDetectNetCoreSdkFeatureBand(
+    __in LPCWSTR wzVariable,
+    __in NETFX_SEARCH* pSearch,
+    __in IBundleExtensionEngine* pEngine,
+    __in LPCWSTR wzBaseDirectory
+    )
+{
+    HRESULT hr = S_OK;
+    LPWSTR sczLatestVersion = NULL;
+
+    hr = DetectNetCoreSdkFeatureBand(
+        pSearch->NetCoreSdkFeatureBandSearch.platform,
+        pSearch->NetCoreSdkFeatureBandSearch.sczMajorVersion,
+        pSearch->NetCoreSdkFeatureBandSearch.sczMinorVersion,
+        pSearch->NetCoreSdkFeatureBandSearch.sczPatchVersion,
+        wzBaseDirectory,
+        &sczLatestVersion);
+    BextExitOnFailure(hr, "DetectNetCoreSdkFeatureBand failed.");
+
+    hr = pEngine->SetVariableVersion(wzVariable, sczLatestVersion);
+    BextExitOnFailure(hr, "Failed to set variable '%ls' to '%ls'", wzVariable, sczLatestVersion);
+
+LExit:
+    ReleaseStr(sczLatestVersion);
+
+    return hr;
+}
diff --git a/src/ext/NetFx/be/detectnetcoresdkfeatureband.h b/src/ext/NetFx/be/detectnetcoresdkfeatureband.h
new file mode 100644
index 00000000..1f92cd58
--- /dev/null
+++ b/src/ext/NetFx/be/detectnetcoresdkfeatureband.h
@@ -0,0 +1,9 @@
+#pragma once
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+HRESULT NetfxPerformDetectNetCoreSdkFeatureBand(
+    __in LPCWSTR wzVariable,
+    __in NETFX_SEARCH* pSearch,
+    __in IBundleExtensionEngine* pEngine,
+    __in LPCWSTR wzBaseDirectory
+    );
diff --git a/src/ext/NetFx/be/netfxbe.vcxproj b/src/ext/NetFx/be/netfxbe.vcxproj
index 0408da72..94026960 100644
--- a/src/ext/NetFx/be/netfxbe.vcxproj
+++ b/src/ext/NetFx/be/netfxbe.vcxproj
@@ -48,20 +48,24 @@
   <ItemGroup>
     <ClCompile Include="detectnetcore.cpp" />
     <ClCompile Include="detectnetcoresdk.cpp" />
+    <ClCompile Include="detectnetcoresdkfeatureband.cpp" />
     <ClCompile Include="netfxbe.cpp" />
     <ClCompile Include="NetfxBundleExtension.cpp" />
     <ClCompile Include="netfxsearch.cpp" />
     <ClCompile Include="precomp.cpp">
       <PrecompiledHeader>Create</PrecompiledHeader>
     </ClCompile>
+    <ClCompile Include="runnetcoresearch.cpp" />
   </ItemGroup>
 
   <ItemGroup>
     <ClInclude Include="detectnetcore.h" />
     <ClInclude Include="detectnetcoresdk.h" />
+    <ClInclude Include="detectnetcoresdkfeatureband.h" />
     <ClInclude Include="NetfxBundleExtension.h" />
     <ClInclude Include="netfxsearch.h" />
     <ClInclude Include="precomp.h" />
+    <ClInclude Include="runnetcoresearch.h" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/ext/NetFx/be/netfxsearch.cpp b/src/ext/NetFx/be/netfxsearch.cpp
index 671e7546..ffbf6ee0 100644
--- a/src/ext/NetFx/be/netfxsearch.cpp
+++ b/src/ext/NetFx/be/netfxsearch.cpp
@@ -15,7 +15,7 @@ STDMETHODIMP NetfxSearchParseFromXml(
     BSTR bstrNodeName = NULL;
 
     // Select Netfx search nodes.
-    hr = XmlSelectNodes(pixnBundleExtension, L"NetFxNetCoreSearch|NetFxNetCoreSdkSearch", &pixnNodes);
+    hr = XmlSelectNodes(pixnBundleExtension, L"NetFxNetCoreSearch|NetFxNetCoreSdkSearch|NetFxNetCoreSdkFeatureBandSearch", &pixnNodes);
     BextExitOnFailure(hr, "Failed to select Netfx search nodes.");
 
     // Get Netfx search node count.
@@ -72,9 +72,30 @@ STDMETHODIMP NetfxSearchParseFromXml(
             hr = XmlGetAttributeUInt32(pixnNode, L"Platform", reinterpret_cast<DWORD*>(&netCoreSdkSearch.platform));
             BextExitOnFailure(hr, "Failed to get @Platform.");
 
-            // @Version
-            hr = XmlGetAttributeEx(pixnNode, L"Version", &netCoreSdkSearch.sczVersion);
-            BextExitOnFailure(hr, "Failed to get @Version.");
+            // @MajorVersion
+            hr = XmlGetAttributeEx(pixnNode, L"MajorVersion", &netCoreSdkSearch.sczMajorVersion);
+            BextExitOnFailure(hr, "Failed to get @MajorVersion.");
+        }
+        else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"NetFxNetCoreSdkFeatureBandSearch", -1))
+        {
+            pSearch->Type = NETFX_SEARCH_TYPE_NET_CORE_SDK_FEATURE_BAND_SEARCH;
+
+            auto& netCoreSdkSearch = pSearch->NetCoreSdkFeatureBandSearch;
+            // @Platform
+            hr = XmlGetAttributeUInt32(pixnNode, L"Platform", reinterpret_cast<DWORD*>(&netCoreSdkSearch.platform));
+            BextExitOnFailure(hr, "Failed to get @Platform.");
+
+            // @MajorVersion
+            hr = XmlGetAttributeEx(pixnNode, L"MajorVersion", &netCoreSdkSearch.sczMajorVersion);
+            BextExitOnFailure(hr, "Failed to get @MajorVersion.");
+
+            // @MinorVersion
+            hr = XmlGetAttributeEx(pixnNode, L"MinorVersion", &netCoreSdkSearch.sczMinorVersion);
+            BextExitOnFailure(hr, "Failed to get @MinorVersion.");
+
+            // @PatchVersion
+            hr = XmlGetAttributeEx(pixnNode, L"PatchVersion", &netCoreSdkSearch.sczPatchVersion);
+            BextExitOnFailure(hr, "Failed to get @PatchVersion.");
         }
         else
         {
@@ -132,6 +153,9 @@ STDMETHODIMP NetfxSearchExecute(
     case NETFX_SEARCH_TYPE_NET_CORE_SDK_SEARCH:
         hr = NetfxPerformDetectNetCoreSdk(wzVariable, pSearch, pEngine, wzBaseDirectory);
         break;
+    case NETFX_SEARCH_TYPE_NET_CORE_SDK_FEATURE_BAND_SEARCH:
+        hr = NetfxPerformDetectNetCoreSdkFeatureBand(wzVariable, pSearch, pEngine, wzBaseDirectory);
+        break;
     default:
         hr = E_UNEXPECTED;
     }
diff --git a/src/ext/NetFx/be/netfxsearch.h b/src/ext/NetFx/be/netfxsearch.h
index f4e4db01..5793dd55 100644
--- a/src/ext/NetFx/be/netfxsearch.h
+++ b/src/ext/NetFx/be/netfxsearch.h
@@ -9,6 +9,7 @@ enum NETFX_SEARCH_TYPE
     NETFX_SEARCH_TYPE_NONE,
     NETFX_SEARCH_TYPE_NET_CORE_SEARCH,
     NETFX_SEARCH_TYPE_NET_CORE_SDK_SEARCH,
+    NETFX_SEARCH_TYPE_NET_CORE_SDK_FEATURE_BAND_SEARCH,
 };
 
 enum NETFX_NET_CORE_RUNTIME_TYPE
@@ -44,8 +45,15 @@ typedef struct _NETFX_SEARCH
         struct
         {
             NETFX_NET_CORE_PLATFORM platform;
-            LPWSTR sczVersion;
+            LPWSTR sczMajorVersion;
         } NetCoreSdkSearch;
+        struct
+        {
+            NETFX_NET_CORE_PLATFORM platform;
+            LPWSTR sczMajorVersion;
+            LPWSTR sczMinorVersion;
+            LPWSTR sczPatchVersion;
+        } NetCoreSdkFeatureBandSearch;
     };
 } NETFX_SEARCH;
 
diff --git a/src/ext/NetFx/be/precomp.h b/src/ext/NetFx/be/precomp.h
index 4a774200..c164834d 100644
--- a/src/ext/NetFx/be/precomp.h
+++ b/src/ext/NetFx/be/precomp.h
@@ -31,4 +31,6 @@
 #include "netfxsearch.h"
 #include "detectnetcore.h"
 #include "detectnetcoresdk.h"
+#include "detectnetcoresdkfeatureband.h"
 #include "NetfxBundleExtension.h"
+#include "runnetcoresearch.h"
diff --git a/src/ext/NetFx/be/runnetcoresearch.cpp b/src/ext/NetFx/be/runnetcoresearch.cpp
new file mode 100644
index 00000000..8f38e0d2
--- /dev/null
+++ b/src/ext/NetFx/be/runnetcoresearch.cpp
@@ -0,0 +1,102 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+#include "precomp.h"
+
+HRESULT RunNetCoreSearch(
+    __in NETFX_NET_CORE_PLATFORM platform,
+    __in LPCWSTR wzBaseDirectory,
+    __in LPCWSTR wzArguments,
+    __inout LPWSTR* psczLatestVersion
+    )
+{
+    HRESULT hr = S_OK;
+    LPCWSTR wzPlatformName = NULL;
+    LPWSTR sczExePath = NULL;
+    LPWSTR sczCommandLine = NULL;
+    HANDLE hProcess = NULL;
+    HANDLE hStdOutErr = INVALID_HANDLE_VALUE;
+    BYTE* rgbOutput = NULL;
+    DWORD cbOutput = 0;
+    DWORD cbTotalRead = 0;
+    DWORD cbRead = 0;
+    DWORD dwExitCode = 0;
+
+    ReleaseNullStr(*psczLatestVersion);
+
+    switch (platform)
+    {
+    case NETFX_NET_CORE_PLATFORM_ARM64:
+        wzPlatformName = L"arm64";
+        break;
+    case NETFX_NET_CORE_PLATFORM_X64:
+        wzPlatformName = L"x64";
+        break;
+    case NETFX_NET_CORE_PLATFORM_X86:
+        wzPlatformName = L"x86";
+        break;
+    default:
+        BextExitWithRootFailure(hr, E_INVALIDARG, "Unknown platform: %u", platform);
+        break;
+    }
+
+    hr = StrAllocFormatted(&sczExePath, L"%ls%ls\\netcoresearch.exe", wzBaseDirectory, wzPlatformName);
+    BextExitOnFailure(hr, "Failed to build netcoresearch.exe path.");
+
+    hr = StrAllocFormatted(&sczCommandLine, L"\"%ls\" %ls", sczExePath, wzArguments);
+    BextExitOnFailure(hr, "Failed to build netcoresearch.exe command line.");
+
+    hr = ProcExecute(sczExePath, sczCommandLine, &hProcess, NULL, &hStdOutErr);
+    if (HRESULT_FROM_WIN32(ERROR_EXE_MACHINE_TYPE_MISMATCH) == hr)
+    {
+        ExitFunction1(hr = S_FALSE);
+    }
+    BextExitOnFailure(hr, "Failed to run: %ls", sczCommandLine);
+
+    cbOutput = 64;
+
+    rgbOutput = reinterpret_cast<BYTE*>(MemAlloc(cbOutput, TRUE));
+    BextExitOnNull(rgbOutput, hr, E_OUTOFMEMORY, "Failed to alloc output string.");
+
+    while (::ReadFile(hStdOutErr, rgbOutput + cbTotalRead, cbOutput - cbTotalRead, &cbRead, NULL))
+    {
+        cbTotalRead += cbRead;
+
+        if (cbTotalRead == cbOutput)
+        {
+            cbOutput *= 2;
+
+            LPVOID pvNew = MemReAlloc(rgbOutput, cbOutput, TRUE);
+            BextExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to realloc output string.");
+
+            rgbOutput = reinterpret_cast<BYTE*>(pvNew);
+        }
+    }
+
+    if (ERROR_BROKEN_PIPE != ::GetLastError())
+    {
+        BextExitWithLastError(hr, "Failed to read netcoresearch.exe output.");
+    }
+
+    hr = ProcWaitForCompletion(hProcess, INFINITE, &dwExitCode);
+    BextExitOnFailure(hr, "Failed to wait for netcoresearch.exe to exit.");
+
+    if (0 != dwExitCode)
+    {
+        BextExitWithRootFailure(hr, E_UNEXPECTED, "netcoresearch.exe failed with exit code: 0x%x\r\nOutput:\r\n%hs", dwExitCode, rgbOutput);
+    }
+
+    if (*rgbOutput)
+    {
+        hr = StrAllocStringAnsi(psczLatestVersion, reinterpret_cast<LPSTR>(rgbOutput), 0, CP_UTF8);
+        BextExitOnFailure(hr, "Failed to widen output string: %hs", rgbOutput);
+    }
+
+LExit:
+    ReleaseFileHandle(hStdOutErr);
+    ReleaseHandle(hProcess);
+    ReleaseMem(rgbOutput);
+    ReleaseStr(sczCommandLine);
+    ReleaseStr(sczExePath);
+
+    return hr;
+}
diff --git a/src/ext/NetFx/be/runnetcoresearch.h b/src/ext/NetFx/be/runnetcoresearch.h
new file mode 100644
index 00000000..78e6b24a
--- /dev/null
+++ b/src/ext/NetFx/be/runnetcoresearch.h
@@ -0,0 +1,9 @@
+#pragma once
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+HRESULT RunNetCoreSearch(
+    __in NETFX_NET_CORE_PLATFORM platform,
+    __in LPCWSTR wzBaseDirectory,
+    __in LPCWSTR wzArguments,
+    __inout LPWSTR* psczLatestVersion
+    );
diff --git a/src/ext/NetFx/netcoresearch/netcoresearch.cpp b/src/ext/NetFx/netcoresearch/netcoresearch.cpp
index 5cf6d10b..8c788461 100644
--- a/src/ext/NetFx/netcoresearch/netcoresearch.cpp
+++ b/src/ext/NetFx/netcoresearch/netcoresearch.cpp
@@ -2,137 +2,155 @@
 
 #include "precomp.h"
 
-enum class NETCORESEARCHKIND
+enum class NETCORESEARCHTYPE
 {
     None,
     Runtime,
     Sdk,
+    SdkFeatureBand,
 };
 
 struct NETCORESEARCH_STATE
 {
-    NETCORESEARCHKIND Kind = NETCORESEARCHKIND::None;
-    union
-    {
-        struct
-        {
-            LPCWSTR wzTargetName;
-            DWORD dwMajorVersion;
-        } Runtime;
-        struct
-        {
-            DWORD dwMajorVersion;
-            DWORD dwMinorVersion;
-            DWORD dwFeatureBand;
-        }
-         Sdk;
-    } Data;
+    NETCORESEARCHTYPE type;
+    HRESULT hrSearch;
     VERUTIL_VERSION* pVersion;
+
+    struct
+    {
+        LPCWSTR wzTargetName;
+        DWORD dwMajorVersion;
+    } Runtime;
+    struct
+    {
+        DWORD dwMajorVersion;
+    } Sdk;
+    struct
+    {
+        DWORD dwMajorVersion;
+        DWORD dwMinorVersion;
+        DWORD dwPatchVersion;
+    } SdkFeatureBand;
 };
 
+static HRESULT GetSearchStateFromArguments(
+    __in int argc,
+    __in LPWSTR argv[],
+    __in NETCORESEARCH_STATE* pSearchState
+    );
 static HRESULT GetDotnetEnvironmentInfo(
-    __in NETCORESEARCH_STATE& pSearchState,
-    __inout VERUTIL_VERSION** ppVersion
+    __in NETCORESEARCH_STATE* pSearchState
     );
 static void HOSTFXR_CALLTYPE GetDotnetEnvironmentInfoResult(
     __in const hostfxr_dotnet_environment_info* pInfo,
     __in LPVOID pvContext
     );
 
-bool string_equal_invariant(__in PCWSTR const x,__in  PCWSTR const y) { return CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, x, -1, y, -1); }
-
-HRESULT get_search_state_from_arguments(__in int argc, __in LPWSTR argv[], __out NETCORESEARCH_STATE& searchState);
-
 int __cdecl wmain(int argc, LPWSTR argv[])
 {
     HRESULT hr = S_OK;
-    VERUTIL_VERSION* pVersion = NULL;
-    NETCORESEARCH_STATE searchState = {};
-
-    ::SetConsoleCP(CP_UTF8);
+    NETCORESEARCH_STATE searchState = { };
 
     ConsoleInitialize();
 
-    hr = get_search_state_from_arguments(argc, argv, OUT searchState);
-    if (FAILED(hr))
-    {
-        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to parse arguments.");
-    }
+    hr = GetSearchStateFromArguments(argc, argv, &searchState);
+    ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to parse arguments.");
 
-    hr = GetDotnetEnvironmentInfo(searchState, &pVersion);
+    hr = GetDotnetEnvironmentInfo(&searchState);
+    ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to search.");
 
-
-    if (pVersion)
+    if (searchState.pVersion)
     {
-        ConsoleWriteW(CONSOLE_COLOR_NORMAL, pVersion->sczVersion);
+        ConsoleWriteW(CONSOLE_COLOR_NORMAL, searchState.pVersion->sczVersion);
     }
 
 LExit:
-    ReleaseVerutilVersion(pVersion);
+    ReleaseVerutilVersion(searchState.pVersion);
     ConsoleUninitialize();
     return hr;
 }
 
-HRESULT get_search_state_from_arguments(int argc, LPWSTR argv[], __out NETCORESEARCH_STATE& searchState)
+HRESULT GetSearchStateFromArguments(
+    __in int argc,
+    __in LPWSTR argv[],
+    __in NETCORESEARCH_STATE* pSearchState
+    )
 {
     HRESULT hr = S_OK;
-    searchState = {};
-    const auto searchKind = argv[1];
+    LPCWSTR wzSearchKind = NULL;
 
-    if (argc < 3)
+    if (argc < 2)
     {
         ExitFunction1(hr = E_INVALIDARG);
     }
 
+    wzSearchKind = argv[1];
 
-    if (string_equal_invariant(searchKind, L"runtime"))
+    if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzSearchKind, -1, L"runtime", -1))
     {
         if (argc != 4)
         {
             ExitFunction1(hr = E_INVALIDARG);
         }
-        searchState.Kind = NETCORESEARCHKIND::Runtime;
 
-        const PCWSTR majorVersion = argv[2];
-        const PCWSTR targetName = argv[3];
+        LPCWSTR wzMajorVersion = argv[2];
+        LPCWSTR wzTargetName = argv[3];
 
-        auto& data = searchState.Data.Runtime;
+        pSearchState->type = NETCORESEARCHTYPE::Runtime;
 
-        data.wzTargetName = targetName;
-        hr = StrStringToUInt32(majorVersion, 0, reinterpret_cast<UINT*>(&data.dwMajorVersion));
-        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to get target version from: %ls", majorVersion);
+        hr = StrStringToUInt32(wzMajorVersion, 0, reinterpret_cast<UINT*>(&pSearchState->Runtime.dwMajorVersion));
+        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to get target version from: %ls", wzMajorVersion);
+
+        pSearchState->Runtime.wzTargetName = wzTargetName;
     }
-    else if(string_equal_invariant(searchKind, L"sdk"))
+    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzSearchKind, -1, L"sdk", -1))
     {
-        searchState.Kind = NETCORESEARCHKIND::Sdk;
+        if (argc != 3)
+        {
+            ExitFunction1(hr = E_INVALIDARG);
+        }
+
+        LPCWSTR wzMajorVersion = argv[2];
 
-        const PCWSTR version = argv[2];
+        pSearchState->type = NETCORESEARCHTYPE::Sdk;
 
-        VERUTIL_VERSION* sdkVersion = nullptr;
-        hr = VerParseVersion(version, 0, FALSE, &sdkVersion);
-        if (FAILED(hr))
+        hr = StrStringToUInt32(wzMajorVersion, 0, reinterpret_cast<UINT*>(&pSearchState->Sdk.dwMajorVersion));
+        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to get sdk major version from: %ls", wzMajorVersion);
+    }
+    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzSearchKind, -1, L"sdkfeatureband", -1))
+    {
+        if (argc != 5)
         {
-            ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to parse version from: %ls", version);
+            ExitFunction1(hr = E_INVALIDARG);
         }
 
-        auto& data = searchState.Data.Sdk;
+        LPCWSTR wzMajorVersion = argv[2];
+        LPCWSTR wzMinorVersion = argv[3];
+        LPCWSTR wzPatchVersion = argv[4];
+
+        pSearchState->type = NETCORESEARCHTYPE::SdkFeatureBand;
 
-        data.dwMajorVersion = sdkVersion->dwMajor;
-        data.dwMinorVersion = sdkVersion->dwMinor;
-        data.dwFeatureBand = sdkVersion->dwPatch;
+        hr = StrStringToUInt32(wzMajorVersion, 0, reinterpret_cast<UINT*>(&pSearchState->SdkFeatureBand.dwMajorVersion));
+        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to get major version from: %ls", wzMajorVersion);
 
-        VerFreeVersion(sdkVersion);
+        hr = StrStringToUInt32(wzMinorVersion, 0, reinterpret_cast<UINT*>(&pSearchState->SdkFeatureBand.dwMinorVersion));
+        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to get minor version from: %ls", wzMinorVersion);
+
+        hr = StrStringToUInt32(wzPatchVersion, 0, reinterpret_cast<UINT*>(&pSearchState->SdkFeatureBand.dwPatchVersion));
+        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to get patch version from: %ls", wzPatchVersion);
+    }
+    else
+    {
+        pSearchState->type = NETCORESEARCHTYPE::None;
+        ExitFunction1(hr = E_INVALIDARG);
     }
 
 LExit:
     return hr;
 }
 
-
-
 static HRESULT GetDotnetEnvironmentInfo(
-    __in NETCORESEARCH_STATE& state,
-    __inout VERUTIL_VERSION** ppVersion
+    __in NETCORESEARCH_STATE* pState
     )
 {
     HRESULT hr = S_OK;
@@ -156,17 +174,13 @@ static HRESULT GetDotnetEnvironmentInfo(
     pfnGetDotnetEnvironmentInfo = (hostfxr_get_dotnet_environment_info_fn)::GetProcAddress(hModule, "hostfxr_get_dotnet_environment_info");
     ConsoleExitOnNullWithLastError(pfnGetDotnetEnvironmentInfo, hr, CONSOLE_COLOR_RED, "Failed to get address for hostfxr_get_dotnet_environment_info.");
 
-    hr = pfnGetDotnetEnvironmentInfo(NULL, NULL, GetDotnetEnvironmentInfoResult, &state);
+    hr = pfnGetDotnetEnvironmentInfo(NULL, NULL, GetDotnetEnvironmentInfoResult, pState);
     ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to get .NET Core environment info.");
 
-    if (state.pVersion)
-    {
-        *ppVersion = state.pVersion;
-        state.pVersion = NULL;
-    }
+    hr = pState->hrSearch;
+    ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to process .NET Core environment info.");
 
 LExit:
-    ReleaseVerutilVersion(state.pVersion);
     ReleaseStr(sczHostfxrPath);
     ReleaseStr(sczProcessPath);
 
@@ -178,108 +192,145 @@ LExit:
     return hr;
 }
 
-bool matches_feature_band(const int requested, const int actual)
+static HRESULT PerformRuntimeSearch(
+    __in const hostfxr_dotnet_environment_info* pInfo,
+    __in DWORD dwMajorVersion,
+    __in LPCWSTR wzTargetName,
+    __inout VERUTIL_VERSION** ppVersion
+    )
 {
-    // we have not requested a match on feature band, so skip the check
-    if (requested == 0) return true;
+    HRESULT hr = S_OK;
+    VERUTIL_VERSION* pFrameworkVersion = NULL;
+    int nCompare = 0;
+
+    for (size_t i = 0; i < pInfo->framework_count; ++i)
+    {
+        const hostfxr_dotnet_environment_framework_info* pFrameworkInfo = pInfo->frameworks + i;
+        ReleaseVerutilVersion(pFrameworkVersion);
+
+        if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzTargetName, -1, pFrameworkInfo->name, -1))
+        {
+            continue;
+        }
+
+        hr = VerParseVersion(pFrameworkInfo->version, 0, FALSE, &pFrameworkVersion);
+        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to parse framework version: %ls", pFrameworkInfo->version);
+
+        if (pFrameworkVersion->dwMajor != dwMajorVersion)
+        {
+            continue;
+        }
+
+        if (*ppVersion)
+        {
+            hr = VerCompareParsedVersions(*ppVersion, pFrameworkVersion, &nCompare);
+            ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to compare versions.");
 
-    const int requestedBand = requested / 100;
-    const int actualBand = actual  / 100;
+            if (nCompare > -1)
+            {
+                continue;
+            }
+        }
 
-    if (actualBand != requestedBand) return false;
+        ReleaseVerutilVersion(*ppVersion);
+        *ppVersion = pFrameworkVersion;
+        pFrameworkVersion = NULL;
+    }
 
-    return actual >= requested;
+LExit:
+    ReleaseVerutilVersion(pFrameworkVersion);
+
+    return hr;
 }
 
-static void HOSTFXR_CALLTYPE GetDotnetEnvironmentInfoResult(
+static HRESULT PerformSdkSearch(
     __in const hostfxr_dotnet_environment_info* pInfo,
-    __in LPVOID pvContext
+    __in BOOL fFeatureBand,
+    __in DWORD dwMajorVersion,
+    __in DWORD dwMinorVersion,
+    __in DWORD dwPatchVersion,
+    __inout VERUTIL_VERSION** ppVersion
     )
 {
-    NETCORESEARCH_STATE* pState = static_cast<NETCORESEARCH_STATE*>(pvContext);
     HRESULT hr = S_OK;
-    VERUTIL_VERSION* pDotnetVersion = nullptr;
+    VERUTIL_VERSION* pSdkVersion = NULL;
     int nCompare = 0;
+    DWORD dwRequestedBand = dwPatchVersion / 100;
 
-
-    if (pState->Kind == NETCORESEARCHKIND::Sdk)
+    for (size_t i = 0; i < pInfo->sdk_count; ++i)
     {
-        auto& sdkData = pState->Data.Sdk;
-        for (size_t i = 0; i < pInfo->sdk_count; ++i)
-        {
-            const hostfxr_dotnet_environment_sdk_info* pSdkInfo = pInfo->sdks + i;
-            ReleaseVerutilVersion(pDotnetVersion);
+        const hostfxr_dotnet_environment_sdk_info* pSdkInfo = pInfo->sdks + i;
+        ReleaseVerutilVersion(pSdkVersion);
 
-            hr = VerParseVersion(pSdkInfo->version, 0, FALSE, &pDotnetVersion);
-            ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to parse sdk version: %ls", pSdkInfo->version);
+        hr = VerParseVersion(pSdkInfo->version, 0, FALSE, &pSdkVersion);
+        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to parse sdk version: %ls", pSdkInfo->version);
 
-            if (pDotnetVersion->dwMajor != sdkData.dwMajorVersion)
-            {
-                continue;
-            }
-            if (!matches_feature_band(sdkData.dwFeatureBand, pDotnetVersion->dwPatch))
+        if (pSdkVersion->dwMajor != dwMajorVersion)
+        {
+            continue;
+        }
+
+        if (fFeatureBand)
+        {
+            if (pSdkVersion->dwMinor != dwMinorVersion)
             {
                 continue;
             }
 
-            if (pState->pVersion)
+            if ((pSdkVersion->dwPatch / 100) != dwRequestedBand)
             {
-                hr = VerCompareParsedVersions(pState->pVersion, pDotnetVersion, &nCompare);
-                ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to compare versions.");
-
-                if (nCompare > -1)
-                {
-                    continue;
-                }
+                continue;
             }
-
-            ReleaseVerutilVersion(pState->pVersion);
-            pState->pVersion = pDotnetVersion;
-            pDotnetVersion = nullptr;
         }
-    }
-    else if(pState->Kind == NETCORESEARCHKIND::Runtime)
-    {
-        auto& runtimeData = pState->Data.Runtime;
-        for (size_t i = 0; i < pInfo->framework_count; ++i)
+
+        if (*ppVersion)
         {
-            const hostfxr_dotnet_environment_framework_info* pFrameworkInfo = pInfo->frameworks + i;
-            ReleaseVerutilVersion(pDotnetVersion);
+            hr = VerCompareParsedVersions(*ppVersion, pSdkVersion, &nCompare);
+            ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to compare versions.");
 
-            if (string_equal_invariant(runtimeData.wzTargetName, pFrameworkInfo->name))
+            if (nCompare > -1)
             {
                 continue;
             }
+        }
 
-            hr = VerParseVersion(pFrameworkInfo->version, 0, FALSE, &pDotnetVersion);
-            ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to parse framework version: %ls", pFrameworkInfo->version);
+        ReleaseVerutilVersion(*ppVersion);
+        *ppVersion = pSdkVersion;
+        pSdkVersion = NULL;
+    }
 
-            if (pDotnetVersion->dwMajor != runtimeData.dwMajorVersion)
-            {
-                continue;
-            }
+LExit:
+    ReleaseVerutilVersion(pSdkVersion);
 
-            if (pState->pVersion)
-            {
-                hr = VerCompareParsedVersions(pState->pVersion, pDotnetVersion, &nCompare);
-                ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to compare versions.");
+    return hr;
+}
 
-                if (nCompare > -1)
-                {
-                    continue;
-                }
-            }
+static void HOSTFXR_CALLTYPE GetDotnetEnvironmentInfoResult(
+    __in const hostfxr_dotnet_environment_info* pInfo,
+    __in LPVOID pvContext
+    )
+{
+    NETCORESEARCH_STATE* pState = static_cast<NETCORESEARCH_STATE*>(pvContext);
+    HRESULT hr = S_OK;
 
-            ReleaseVerutilVersion(pState->pVersion);
-            pState->pVersion = pDotnetVersion;
-            pDotnetVersion = nullptr;
-        }
+    if (pState->type == NETCORESEARCHTYPE::Sdk)
+    {
+        hr = PerformSdkSearch(pInfo, FALSE, pState->Sdk.dwMajorVersion, 0, 0, &pState->pVersion);
+    }
+    else if (pState->type == NETCORESEARCHTYPE::SdkFeatureBand)
+    {
+        hr = PerformSdkSearch(pInfo, TRUE, pState->SdkFeatureBand.dwMajorVersion, pState->SdkFeatureBand.dwMinorVersion, pState->SdkFeatureBand.dwPatchVersion, &pState->pVersion);
+    }
+    else if (pState->type == NETCORESEARCHTYPE::Runtime)
+    {
+        hr = PerformRuntimeSearch(pInfo, pState->Runtime.dwMajorVersion, pState->Runtime.wzTargetName, &pState->pVersion);
     }
     else
     {
-        ConsoleWriteError(E_INVALIDARG, CONSOLE_COLOR_RED, "Invalid NETCORESEARCHKIND.");
+        hr = E_INVALIDARG;
+        ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Invalid NETCORESEARCHTYPE.");
     }
 
 LExit:
-    ReleaseVerutilVersion(pDotnetVersion);
+    pState->hrSearch = hr;
 }
diff --git a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/BundleLatest.wxs b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/BundleLatest.wxs
index b7b55747..812f623b 100644
--- a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/BundleLatest.wxs
+++ b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/BundleLatest.wxs
@@ -7,9 +7,11 @@
             <PackageGroupRef Id="AspNetCoreRuntime3112Redist_x86" />
             <PackageGroupRef Id="DesktopNetCoreRuntime3112Redist_x86" />
             <PackageGroupRef Id="DotNetCoreRuntime3112Redist_x86" />
+            <PackageGroupRef Id="DotNetCoreSdk31425Redist_x86" />
             <PackageGroupRef Id="AspNetCoreRuntime3112Redist_x64" />
             <PackageGroupRef Id="DesktopNetCoreRuntime3112Redist_x64" />
             <PackageGroupRef Id="DotNetCoreRuntime3112Redist_x64" />
+            <PackageGroupRef Id="DotNetCoreSdk31425Redist_x64" />
         </Chain>
     </Bundle>
 </Wix>
diff --git a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/BundleLatest_x64.wxs b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/BundleLatest_x64.wxs
index 3588686a..a4639d7c 100644
--- a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/BundleLatest_x64.wxs
+++ b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/BundleLatest_x64.wxs
@@ -7,6 +7,7 @@
             <PackageGroupRef Id="AspNetCoreRuntime3112Redist_x64" />
             <PackageGroupRef Id="DesktopNetCoreRuntime3112Redist_x64" />
             <PackageGroupRef Id="DotNetCoreRuntime3112Redist_x64" />
+            <PackageGroupRef Id="DotNetCoreSdk31425Redist_x64" />
         </Chain>
     </Bundle>
 </Wix>
diff --git a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3.1.12_x64.wxs b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3.1.12_x64.wxs
index 74e82405..78444901 100644
--- a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3.1.12_x64.wxs
+++ b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3.1.12_x64.wxs
@@ -5,7 +5,7 @@
     <?define NetCorePlatform = x64?>
     <?define NetCoreIdVersion = 3112?>
     <?define NetCoreVersion = 3.1.12?>
-    <?define NetCoreSdkPlatform = x64?>
+    <?define NetCoreSdkFeatureBandVersion = 31400?>
     <?define NetCoreSdkIdVersion = 31425?>
     <?define NetCoreSdkVersion = 3.1.425?>
     <?include NetCore3_Platform.wxi?>
@@ -61,16 +61,17 @@
     </Fragment>
 
     <Fragment>
-        <netfx:DotNetCoreSdkSearch Id="$(var.DotNetCoreSdkId)" Platform="$(var.NetCoreSdkPlatform)" Version="3.0.100" Variable="$(var.DotNetCoreSdkId)" />
+        <netfx:DotNetCoreSdkSearch Id="$(var.DotNetCoreSdkId)" Platform="$(var.NetCorePlatform)" MajorVersion="3" Variable="$(var.DotNetCoreSdkId)" />
+        <netfx:DotNetCoreSdkFeatureBandSearch Id="$(var.DotNetCoreSdkFeatureBandId)" Platform="$(var.NetCorePlatform)" Version="3.1.400" Variable="$(var.DotNetCoreSdkFeatureBandId)" />
 
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)DetectCondition" Value="$(var.DotNetCoreSdkId) &gt;= v$(var.NetCoreSdkVersion)" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)InstallCondition" Value="" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)PackageDirectory" Value="redist\" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)RepairArguments" Value="" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)DetectCondition" Value="$(var.DotNetCoreSdkFeatureBandId) &gt;= v$(var.NetCoreSdkVersion)" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)InstallCondition" Value="" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)PackageDirectory" Value="redist\" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)RepairArguments" Value="" Overridable="yes" />
 
         <PackageGroup Id="$(var.DotNetCoreSdkRedistId)">
-            <ExePackage CacheId="$(var.DotNetCoreSdkRedistId)_2485A7AFA98E178CB8F30C9838346B514AEA4769" InstallArguments="$(var.DotNetCoreSdkRedistInstallArguments)" PerMachine="yes" DetectCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)DetectCondition)" InstallCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)InstallCondition)" Id="$(var.DotNetCoreSdkRedistId)" Vital="yes" Permanent="yes" Protocol="burn" LogPathVariable="$(var.DotNetCoreSdkRedistLog)" Cache="remove">
-                <ExePackagePayload Name="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)PackageDirectory)dotnet-sdk-$(var.NetCoreSdkVersion)-win-$(var.NetCoreSdkPlatform).exe" DownloadUrl="$(var.DotNetCoreSdkRedistLink)" ProductName="Microsoft .NET Core Runtime - 3.1.425 (x64)" Description="Microsoft .NET Core Runtime - 3.1.425 (x64)" CertificatePublicKey="3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18" CertificateThumbprint="2485A7AFA98E178CB8F30C9838346B514AEA4769" Size="135024384" Version="3.1.425" />
+            <ExePackage CacheId="$(var.DotNetCoreSdkRedistId)_2485A7AFA98E178CB8F30C9838346B514AEA4769" InstallArguments="$(var.DotNetCoreSdkRedistInstallArguments)" PerMachine="yes" DetectCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)DetectCondition)" InstallCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)InstallCondition)" Id="$(var.DotNetCoreSdkRedistId)" Vital="yes" Permanent="yes" Protocol="burn" LogPathVariable="$(var.DotNetCoreSdkRedistLog)" Cache="remove">
+                <ExePackagePayload Name="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)PackageDirectory)dotnet-sdk-$(var.NetCoreSdkVersion)-win-$(var.NetCorePlatform).exe" DownloadUrl="$(var.DotNetCoreSdkRedistLink)" ProductName="Microsoft .NET Core Runtime - 3.1.425 (x64)" Description="Microsoft .NET Core Runtime - 3.1.425 (x64)" CertificatePublicKey="3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18" CertificateThumbprint="2485A7AFA98E178CB8F30C9838346B514AEA4769" Size="135024384" Version="3.1.425" />
             </ExePackage>
         </PackageGroup>
     </Fragment>
diff --git a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3.1.12_x86.wxs b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3.1.12_x86.wxs
index 9acc4dc5..69700d80 100644
--- a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3.1.12_x86.wxs
+++ b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3.1.12_x86.wxs
@@ -5,7 +5,7 @@
     <?define NetCorePlatform = x86?>
     <?define NetCoreIdVersion = 3112?>
     <?define NetCoreVersion = 3.1.12?>
-    <?define NetCoreSdkPlatform = x86?>
+    <?define NetCoreSdkFeatureBandVersion = 31400?>
     <?define NetCoreSdkIdVersion = 31425?>
     <?define NetCoreSdkVersion = 3.1.425?>
     <?include NetCore3_Platform.wxi?>
@@ -61,16 +61,17 @@
     </Fragment>
 
     <Fragment>
-        <netfx:DotNetCoreSdkSearch Id="$(var.DotNetCoreSdkId)" Platform="$(var.NetCoreSdkPlatform)" Version="3.0.100" Variable="$(var.DotNetCoreSdkId)" />
+        <netfx:DotNetCoreSdkSearch Id="$(var.DotNetCoreSdkId)" Platform="$(var.NetCorePlatform)" MajorVersion="3" Variable="$(var.DotNetCoreSdkId)" />
+        <netfx:DotNetCoreSdkFeatureBandSearch Id="$(var.DotNetCoreSdkFeatureBandId)" Platform="$(var.NetCorePlatform)" Version="3.1.400" Variable="$(var.DotNetCoreSdkFeatureBandId)" />
 
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)DetectCondition" Value="$(var.DotNetCoreSdkId) &gt;= v$(var.NetCoreSdkVersion)" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)InstallCondition" Value="" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)PackageDirectory" Value="redist\" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)RepairArguments" Value="" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)DetectCondition" Value="$(var.DotNetCoreSdkFeatureBandId) &gt;= v$(var.NetCoreSdkVersion)" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)InstallCondition" Value="" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)PackageDirectory" Value="redist\" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)RepairArguments" Value="" Overridable="yes" />
 
         <PackageGroup Id="$(var.DotNetCoreSdkRedistId)">
-            <ExePackage CacheId="$(var.DotNetCoreSdkRedistId)_2485A7AFA98E178CB8F30C9838346B514AEA4769" InstallArguments="$(var.DotNetCoreSdkRedistInstallArguments)" PerMachine="yes" DetectCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)DetectCondition)" InstallCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)InstallCondition)" Id="$(var.DotNetCoreSdkRedistId)" Vital="yes" Permanent="yes" Protocol="burn" LogPathVariable="$(var.DotNetCoreSdkRedistLog)" Cache="remove">
-                <ExePackagePayload Name="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)PackageDirectory)dotnet-sdk-$(var.NetCoreSdkVersion)-win-$(var.NetCoreSdkPlatform).exe" DownloadUrl="$(var.DotNetCoreSdkRedistLink)" ProductName="Microsoft .NET Core SDK - 3.1.425 (x86)" Description="Microsoft .NET Core Runtime - 3.1.12 (x86)" CertificatePublicKey="3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18" CertificateThumbprint="2485A7AFA98E178CB8F30C9838346B514AEA4769" Size="124974936" Version="3.1.425" />
+            <ExePackage CacheId="$(var.DotNetCoreSdkRedistId)_2485A7AFA98E178CB8F30C9838346B514AEA4769" InstallArguments="$(var.DotNetCoreSdkRedistInstallArguments)" PerMachine="yes" DetectCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)DetectCondition)" InstallCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)InstallCondition)" Id="$(var.DotNetCoreSdkRedistId)" Vital="yes" Permanent="yes" Protocol="burn" LogPathVariable="$(var.DotNetCoreSdkRedistLog)" Cache="remove">
+                <ExePackagePayload Name="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)PackageDirectory)dotnet-sdk-$(var.NetCoreSdkVersion)-win-$(var.NetCorePlatform).exe" DownloadUrl="$(var.DotNetCoreSdkRedistLink)" ProductName="Microsoft .NET Core SDK - 3.1.425 (x86)" Description="Microsoft .NET Core Runtime - 3.1.12 (x86)" CertificatePublicKey="3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18" CertificateThumbprint="2485A7AFA98E178CB8F30C9838346B514AEA4769" Size="124974936" Version="3.1.425" />
             </ExePackage>
         </PackageGroup>
     </Fragment>
diff --git a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3_Platform.wxi b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3_Platform.wxi
index 2933196d..affe0143 100644
--- a/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3_Platform.wxi
+++ b/src/ext/NetFx/test/WixToolsetTest.Netfx/TestData/UsingDotNetCorePackages/NetCore3_Platform.wxi
@@ -21,8 +21,9 @@
   <?define DotNetCoreRedistInstallArguments = /install /quiet /log "[$(var.DotNetCoreRedistLog)]"?>
   <?define DotNetCoreRedistUninstallArguments = /uninstall /quiet /log "[$(var.DotNetCoreRedistLog)]"?>
 
-  <?define DotNetCoreSdkId = DOTNETCORESDK3_$(var.NetCoreSdkPlatform)?>
-  <?define DotNetCoreSdkRedistId = DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist_$(var.NetCoreSdkPlatform)?>
+  <?define DotNetCoreSdkId = DOTNETCORESDK3_$(var.NetCorePlatform)?>
+  <?define DotNetCoreSdkFeatureBandId = DOTNETCORESDK$(var.NetCoreSdkFeatureBandVersion)_$(var.NetCorePlatform)?>
+  <?define DotNetCoreSdkRedistId = DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist_$(var.NetCorePlatform)?>
   <?define DotNetCoreSdkRedistLog = DotNetCoreSdk$(var.NetCoreSdkIdVersion)RedistLog?>
   <?define DotNetCoreSdkRedistInstallArguments = /install /quiet /log "[$(var.DotNetCoreSdkRedistLog)]"?>
   <?define DotNetCoreSdkRedistUninstallArguments = /uninstall /quiet /log "[$(var.DotNetCoreSdkRedistLog)]"?>
diff --git a/src/ext/NetFx/wixext/NetFxCompiler.cs b/src/ext/NetFx/wixext/NetFxCompiler.cs
index 7a37cf90..c40bc77b 100644
--- a/src/ext/NetFx/wixext/NetFxCompiler.cs
+++ b/src/ext/NetFx/wixext/NetFxCompiler.cs
@@ -55,6 +55,12 @@ namespace WixToolset.Netfx
                         case "DotNetCoreSdkSearchRef":
                             this.ParseDotNetCoreSdkSearchRefElement(intermediate, section, element);
                             break;
+                        case "DotNetCoreSdkFeatureBandSearch":
+                            this.ParseDotNetCoreSdkFeatureBandSearchElement(intermediate, section, element);
+                            break;
+                        case "DotNetCoreSdkFeatureBandSearchRef":
+                            this.ParseDotNetCoreSdkFeatureBandSearchRefElement(intermediate, section, element);
+                            break;
                         case "DotNetCompatibilityCheck":
                             this.ParseDotNetCompatibilityCheckElement(intermediate, section, element);
                             break;
@@ -81,6 +87,12 @@ namespace WixToolset.Netfx
                         case "DotNetCoreSdkSearchRef":
                             this.ParseDotNetCoreSdkSearchRefElement(intermediate, section, element);
                             break;
+                        case "DotNetCoreSdkFeatureBandSearch":
+                            this.ParseDotNetCoreSdkFeatureBandSearchElement(intermediate, section, element);
+                            break;
+                        case "DotNetCoreSdkFeatureBandSearchRef":
+                            this.ParseDotNetCoreSdkFeatureBandSearchRefElement(intermediate, section, element);
+                            break;
                         default:
                             this.ParseHelper.UnexpectedElement(parentElement, element);
                             break;
@@ -267,8 +279,8 @@ namespace WixToolset.Netfx
             string variable = null;
             string condition = null;
             string after = null;
-            NetCoreSdkSearchPlatform? platform = null;
-            string version = null;
+            NetCoreSearchPlatform? platform = null;
+            var majorVersion = CompilerConstants.IntegerNotSet;
 
             foreach (var attrib in element.Attributes())
             {
@@ -293,22 +305,22 @@ namespace WixToolset.Netfx
                             switch (platformValue)
                             {
                                 case "arm64":
-                                    platform = NetCoreSdkSearchPlatform.Arm64;
+                                    platform = NetCoreSearchPlatform.Arm64;
                                     break;
                                 case "x64":
-                                    platform = NetCoreSdkSearchPlatform.X64;
+                                    platform = NetCoreSearchPlatform.X64;
                                     break;
                                 case "x86":
-                                    platform = NetCoreSdkSearchPlatform.X86;
+                                    platform = NetCoreSearchPlatform.X86;
                                     break;
                                 default:
                                     this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Platform", platformValue, "arm64", "x64", "x86"));
                                     break;
                             }
                             break;
-                        case "Version":
+                        case "MajorVersion":
                             // .NET Core had a different deployment strategy before .NET Core 3.0 which would require different detection logic.
-                            version = this.ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib);
+                            majorVersion = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 3, Int32.MaxValue);
                             break;
                         default:
                             this.ParseHelper.UnexpectedAttribute(element, attrib);
@@ -331,16 +343,13 @@ namespace WixToolset.Netfx
                 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Platform"));
             }
 
-            
-            if (String.IsNullOrEmpty(version))
+            if (majorVersion == CompilerConstants.IntegerNotSet)
             {
-                this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Version"));
+                this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "MajorVersion"));
             }
-
-            var ver = Version.Parse(version);
-            if (ver.Major == 4)
+            else if (majorVersion == 4)
             {
-                this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Version", version, "3.*", "5+.*"));
+                this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "MajorVersion", "4", "3", "5+"));
             }
 
             this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
@@ -358,7 +367,7 @@ namespace WixToolset.Netfx
                 section.AddSymbol(new NetFxNetCoreSdkSearchSymbol(sourceLineNumbers, id)
                 {
                     Platform = platform.Value,
-                    Version = version,
+                    MajorVersion = majorVersion,
                 });
             }
         }
@@ -391,6 +400,157 @@ namespace WixToolset.Netfx
             this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
         }
 
+        private void ParseDotNetCoreSdkFeatureBandSearchElement(Intermediate intermediate, IntermediateSection section, XElement element)
+        {
+            var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
+            Identifier id = null;
+            string variable = null;
+            string condition = null;
+            string after = null;
+            NetCoreSearchPlatform? platform = null;
+            string version = null;
+            var majorVersion = 0;
+            var minorVersion = 0;
+            var patchVersion = 0;
+
+            foreach (var attrib in element.Attributes())
+            {
+                if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
+                {
+                    switch (attrib.Name.LocalName)
+                    {
+                        case "Id":
+                            id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
+                            break;
+                        case "Variable":
+                            variable = this.ParseHelper.GetAttributeBundleVariableNameValue(sourceLineNumbers, attrib);
+                            break;
+                        case "Condition":
+                            condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
+                            break;
+                        case "After":
+                            after = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
+                            break;
+                        case "Platform":
+                            var platformValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
+                            switch (platformValue)
+                            {
+                                case "arm64":
+                                    platform = NetCoreSearchPlatform.Arm64;
+                                    break;
+                                case "x64":
+                                    platform = NetCoreSearchPlatform.X64;
+                                    break;
+                                case "x86":
+                                    platform = NetCoreSearchPlatform.X86;
+                                    break;
+                                default:
+                                    this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Platform", platformValue, "arm64", "x64", "x86"));
+                                    break;
+                            }
+                            break;
+                        case "Version":
+                            version = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
+                            break;
+                        default:
+                            this.ParseHelper.UnexpectedAttribute(element, attrib);
+                            break;
+                    }
+                }
+                else
+                {
+                    this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
+                }
+            }
+
+            if (id == null)
+            {
+                id = this.ParseHelper.CreateIdentifier("dncsfbs", variable, condition, after);
+            }
+
+            if (!platform.HasValue)
+            {
+                this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Platform"));
+            }
+
+            if (String.IsNullOrEmpty(version))
+            {
+                this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Version"));
+            }
+            else
+            {
+                if (!Version.TryParse(version, out var featureBandVersion))
+                {
+                    this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Version", version, "x.x.x00"));
+                }
+                else
+                {
+                    majorVersion = featureBandVersion.Major;
+                    minorVersion = featureBandVersion.Minor;
+                    patchVersion = featureBandVersion.Build;
+
+                    if ((patchVersion % 100) != 0 || featureBandVersion.Revision != -1)
+                    {
+                        this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Version", version, "x.x.x00"));
+                    }
+
+                    if (majorVersion == 4)
+                    {
+                        this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Version", version, "3.*", "5+.*"));
+                    }
+                }
+            }
+
+            this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
+
+            var bundleExtensionId = this.ParseHelper.CreateIdentifierValueFromPlatform("Wix4NetfxBundleExtension", this.Context.Platform, BurnPlatforms.X86 | BurnPlatforms.X64 | BurnPlatforms.ARM64);
+            if (bundleExtensionId == null)
+            {
+                this.Messaging.Write(ErrorMessages.UnsupportedPlatformForElement(sourceLineNumbers, this.Context.Platform.ToString(), element.Name.LocalName));
+            }
+
+            if (!this.Messaging.EncounteredError)
+            {
+                this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, bundleExtensionId);
+
+                section.AddSymbol(new NetFxNetCoreSdkFeatureBandSearchSymbol(sourceLineNumbers, id)
+                {
+                    Platform = platform.Value,
+                    MajorVersion = majorVersion,
+                    MinorVersion = minorVersion,
+                    PatchVersion = patchVersion,
+                });
+            }
+        }
+
+        private void ParseDotNetCoreSdkFeatureBandSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element)
+        {
+            var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
+
+            foreach (var attrib in element.Attributes())
+            {
+                if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
+                {
+                    switch (attrib.Name.LocalName)
+                    {
+                        case "Id":
+                            var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
+                            this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, NetfxSymbolDefinitions.NetFxNetCoreSdkFeatureBandSearch, refId);
+                            break;
+                        default:
+                            this.ParseHelper.UnexpectedAttribute(element, attrib);
+                            break;
+                    }
+                }
+                else
+                {
+                    this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
+                }
+            }
+
+            this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
+        }
+
         /// <summary>
         /// Parses a NativeImage element.
         /// </summary>
diff --git a/src/ext/NetFx/wixext/Symbols/NetFxNetCoreSearchSdkFeatureBandSymbol.cs b/src/ext/NetFx/wixext/Symbols/NetFxNetCoreSearchSdkFeatureBandSymbol.cs
new file mode 100644
index 00000000..ad80f9d2
--- /dev/null
+++ b/src/ext/NetFx/wixext/Symbols/NetFxNetCoreSearchSdkFeatureBandSymbol.cs
@@ -0,0 +1,71 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+namespace WixToolset.Netfx
+{
+    using WixToolset.Data;
+    using WixToolset.Netfx.Symbols;
+
+    public static partial class NetfxSymbolDefinitions
+    {
+        public static readonly IntermediateSymbolDefinition NetFxNetCoreSdkFeatureBandSearch = new IntermediateSymbolDefinition(
+            NetfxSymbolDefinitionType.NetFxNetCoreSdkFeatureBandSearch.ToString(),
+            new[]
+            {
+                new IntermediateFieldDefinition(nameof(NetFxNetCoreSdkFeatureBandSearchSymbolFields.Platform), IntermediateFieldType.Number),
+                new IntermediateFieldDefinition(nameof(NetFxNetCoreSdkFeatureBandSearchSymbolFields.MajorVersion), IntermediateFieldType.Number),
+                new IntermediateFieldDefinition(nameof(NetFxNetCoreSdkFeatureBandSearchSymbolFields.MinorVersion), IntermediateFieldType.Number),
+                new IntermediateFieldDefinition(nameof(NetFxNetCoreSdkFeatureBandSearchSymbolFields.PatchVersion), IntermediateFieldType.Number),
+            },
+            typeof(NetFxNetCoreSdkFeatureBandSearchSymbol));
+    }
+}
+
+namespace WixToolset.Netfx.Symbols
+{
+    using WixToolset.Data;
+
+    public enum NetFxNetCoreSdkFeatureBandSearchSymbolFields
+    {
+        Platform,
+        MajorVersion,
+        MinorVersion,
+        PatchVersion,
+    }
+
+    public class NetFxNetCoreSdkFeatureBandSearchSymbol : IntermediateSymbol
+    {
+        public NetFxNetCoreSdkFeatureBandSearchSymbol() : base(NetfxSymbolDefinitions.NetFxNetCoreSdkFeatureBandSearch, null, null)
+        {
+        }
+
+        public NetFxNetCoreSdkFeatureBandSearchSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(NetfxSymbolDefinitions.NetFxNetCoreSdkFeatureBandSearch, sourceLineNumber, id)
+        {
+        }
+
+        public IntermediateField this[NetFxNetCoreSdkFeatureBandSearchSymbolFields index] => this.Fields[(int)index];
+
+        public NetCoreSearchPlatform Platform
+        {
+            get => (NetCoreSearchPlatform)this.Fields[(int)NetFxNetCoreSdkFeatureBandSearchSymbolFields.Platform].AsNumber();
+            set => this.Set((int)NetFxNetCoreSdkFeatureBandSearchSymbolFields.Platform, (int)value);
+        }
+
+        public int MajorVersion
+        {
+            get => this.Fields[(int)NetFxNetCoreSdkFeatureBandSearchSymbolFields.MajorVersion].AsNumber();
+            set => this.Set((int)NetFxNetCoreSdkFeatureBandSearchSymbolFields.MajorVersion, value);
+        }
+
+        public int MinorVersion
+        {
+            get => this.Fields[(int)NetFxNetCoreSdkFeatureBandSearchSymbolFields.MinorVersion].AsNumber();
+            set => this.Set((int)NetFxNetCoreSdkFeatureBandSearchSymbolFields.MinorVersion, value);
+        }
+
+        public int PatchVersion
+        {
+            get => this.Fields[(int)NetFxNetCoreSdkFeatureBandSearchSymbolFields.PatchVersion].AsNumber();
+            set => this.Set((int)NetFxNetCoreSdkFeatureBandSearchSymbolFields.PatchVersion, value);
+        }
+    }
+}
diff --git a/src/ext/NetFx/wixext/Symbols/NetFxNetCoreSearchSdkSymbol.cs b/src/ext/NetFx/wixext/Symbols/NetFxNetCoreSearchSdkSymbol.cs
index 86b750ea..a368068e 100644
--- a/src/ext/NetFx/wixext/Symbols/NetFxNetCoreSearchSdkSymbol.cs
+++ b/src/ext/NetFx/wixext/Symbols/NetFxNetCoreSearchSdkSymbol.cs
@@ -12,9 +12,9 @@ namespace WixToolset.Netfx
             new[]
             {
                 new IntermediateFieldDefinition(nameof(NetFxNetCoreSdkSearchSymbolFields.Platform), IntermediateFieldType.Number),
-                new IntermediateFieldDefinition(nameof(NetFxNetCoreSdkSearchSymbolFields.Version), IntermediateFieldType.String),
+                new IntermediateFieldDefinition(nameof(NetFxNetCoreSdkSearchSymbolFields.MajorVersion), IntermediateFieldType.Number),
             },
-            typeof(NetFxNetCoreSearchSymbol));
+            typeof(NetFxNetCoreSdkSearchSymbol));
     }
 }
 
@@ -22,21 +22,12 @@ namespace WixToolset.Netfx.Symbols
 {
     using WixToolset.Data;
 
-
-    public enum NetCoreSdkSearchPlatform
-    {
-        X86,
-        X64,
-        Arm64,
-    }
-
     public enum NetFxNetCoreSdkSearchSymbolFields
     {
         Platform,
-        Version,
+        MajorVersion,
     }
 
-
     public class NetFxNetCoreSdkSearchSymbol : IntermediateSymbol
     {
         public NetFxNetCoreSdkSearchSymbol() : base(NetfxSymbolDefinitions.NetFxNetCoreSdkSearch, null, null)
@@ -49,16 +40,16 @@ namespace WixToolset.Netfx.Symbols
 
         public IntermediateField this[NetFxNetCoreSdkSearchSymbolFields index] => this.Fields[(int)index];
 
-        public NetCoreSdkSearchPlatform Platform
+        public NetCoreSearchPlatform Platform
         {
-            get => (NetCoreSdkSearchPlatform)this.Fields[(int)NetFxNetCoreSdkSearchSymbolFields.Platform].AsNumber();
+            get => (NetCoreSearchPlatform)this.Fields[(int)NetFxNetCoreSdkSearchSymbolFields.Platform].AsNumber();
             set => this.Set((int)NetFxNetCoreSdkSearchSymbolFields.Platform, (int)value);
         }
 
-        public string Version
+        public int MajorVersion
         {
-            get => this.Fields[(int)NetFxNetCoreSdkSearchSymbolFields.Version].AsString();
-            set => this.Set((int)NetFxNetCoreSdkSearchSymbolFields.Version, value);
+            get => this.Fields[(int)NetFxNetCoreSdkSearchSymbolFields.MajorVersion].AsNumber();
+            set => this.Set((int)NetFxNetCoreSdkSearchSymbolFields.MajorVersion, value);
         }
     }
 }
diff --git a/src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs b/src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs
index 00c52f9e..1db4f647 100644
--- a/src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs
+++ b/src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs
@@ -10,8 +10,9 @@ namespace WixToolset.Netfx
     {
         NetFxNativeImage,
         NetFxNetCoreSearch,
-        NetFxNetCoreSdkSearch,
         NetFxDotNetCompatibilityCheck,
+        NetFxNetCoreSdkSearch,
+        NetFxNetCoreSdkFeatureBandSearch,
     }
 
     public static partial class NetfxSymbolDefinitions
@@ -41,6 +42,9 @@ namespace WixToolset.Netfx
                 case NetfxSymbolDefinitionType.NetFxNetCoreSdkSearch:
                     return NetfxSymbolDefinitions.NetFxNetCoreSdkSearch;
 
+                case NetfxSymbolDefinitionType.NetFxNetCoreSdkFeatureBandSearch:
+                    return NetfxSymbolDefinitions.NetFxNetCoreSdkFeatureBandSearch;
+
                 case NetfxSymbolDefinitionType.NetFxDotNetCompatibilityCheck:
                     return NetfxSymbolDefinitions.NetFxDotNetCompatibilityCheck;
 
@@ -53,6 +57,7 @@ namespace WixToolset.Netfx
         {
             NetFxNetCoreSearch.AddTag(BurnConstants.BundleExtensionSearchSymbolDefinitionTag);
             NetFxNetCoreSdkSearch.AddTag(BurnConstants.BundleExtensionSearchSymbolDefinitionTag);
+            NetFxNetCoreSdkFeatureBandSearch.AddTag(BurnConstants.BundleExtensionSearchSymbolDefinitionTag);
         }
     }
 }
diff --git a/src/test/burn/TestData/TestBA/TestBAWixlib/NetCore6.0.9_x86.wxs b/src/test/burn/TestData/TestBA/TestBAWixlib/NetCore6.0.9_x86.wxs
index c2232674..5840bf61 100644
--- a/src/test/burn/TestData/TestBA/TestBAWixlib/NetCore6.0.9_x86.wxs
+++ b/src/test/burn/TestData/TestBA/TestBAWixlib/NetCore6.0.9_x86.wxs
@@ -5,7 +5,7 @@
     <?define NetCorePlatform = x86?>
     <?define NetCoreIdVersion = 609?>
     <?define NetCoreVersion = 6.0.9?>
-    <?define NetCoreSdkPlatform = x86?>
+    <?define NetCoreSdkFeatureBandVersion = 60400?>
     <?define NetCoreSdkIdVersion = 60403?>
     <?define NetCoreSdkVersion = 6.0.403?>
     <?include NetCore6_Platform.wxi?>
@@ -61,16 +61,21 @@
     </Fragment>
 
     <Fragment>
-        <netfx:DotNetCoreSdkSearch Id="$(var.DotNetCoreSdkId)" Platform="$(var.NetCoreSdkPlatform)" Version="6.0.200" Variable="$(var.DotNetCoreSdkId)" />
+        <netfx:DotNetCoreSdkSearch Id="$(var.DotNetCoreSdkId)" Platform="$(var.NetCorePlatform)" MajorVersion="6" Variable="$(var.DotNetCoreSdkId)" />
+        <netfx:DotNetCoreSdkFeatureBandSearch Id="$(var.DotNetCoreSdkFeatureBandId)" Platform="$(var.NetCorePlatform)" Version="6.0.400" Variable="$(var.DotNetCoreSdkFeatureBandId)" />
+    </Fragment>
+
+    <Fragment>
+        <netfx:DotNetCoreSdkFeatureBandSearchRef Id="$(var.DotNetCoreSdkFeatureBandId)" />
 
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)DetectCondition" Value="$(var.DotNetCoreSdkId) &gt;= v$(var.NetCoreSdkVersion)" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)InstallCondition" Value="" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)PackageDirectory" Value="redist\" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)RepairArguments" Value="" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)DetectCondition" Value="$(var.DotNetCoreSdkFeatureBandId) &gt;= v$(var.NetCoreSdkVersion)" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)InstallCondition" Value="" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)PackageDirectory" Value="redist\" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)RepairArguments" Value="" Overridable="yes" />
 
         <PackageGroup Id="$(var.DotNetCoreSdkRedistId)">
-            <ExePackage CacheId="$(var.DotNetCoreSdkRedistId)_2485A7AFA98E178CB8F30C9838346B514AEA4769" InstallArguments="$(var.DotNetCoreSdkRedistInstallArguments)" PerMachine="yes" DetectCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)DetectCondition)" InstallCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)InstallCondition)" Id="$(var.DotNetCoreSdkRedistId)" Vital="yes" Permanent="yes" Protocol="burn" LogPathVariable="$(var.DotNetCoreSdkRedistLog)" Cache="remove">
-                <ExePackagePayload Name="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)PackageDirectory)dotnet-sdk-$(var.NetCoreSdkVersion)-win-$(var.NetCoreSdkPlatform).exe" DownloadUrl="$(var.DotNetCoreSdkRedistLink)" ProductName="Microsoft .NET 6.0 SDK - 6.0.403 (x86)" Description="Microsoft .NET SDK - 6.0.403 (x86)" CertificatePublicKey="3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18" CertificateThumbprint="2485A7AFA98E178CB8F30C9838346B514AEA4769" Size="194236520" Version="6.0.403" />
+            <ExePackage CacheId="$(var.DotNetCoreSdkRedistId)_2485A7AFA98E178CB8F30C9838346B514AEA4769" InstallArguments="$(var.DotNetCoreSdkRedistInstallArguments)" PerMachine="yes" DetectCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)DetectCondition)" InstallCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)InstallCondition)" Id="$(var.DotNetCoreSdkRedistId)" Vital="yes" Permanent="yes" Protocol="burn" LogPathVariable="$(var.DotNetCoreSdkRedistLog)" Cache="remove">
+                <ExePackagePayload Name="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)PackageDirectory)dotnet-sdk-$(var.NetCoreSdkVersion)-win-$(var.NetCorePlatform).exe" DownloadUrl="$(var.DotNetCoreSdkRedistLink)" ProductName="Microsoft .NET 6.0 SDK - 6.0.403 (x86)" Description="Microsoft .NET SDK - 6.0.403 (x86)" CertificatePublicKey="3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18" CertificateThumbprint="2485A7AFA98E178CB8F30C9838346B514AEA4769" Size="194236520" Version="6.0.403" />
             </ExePackage>
         </PackageGroup>
     </Fragment>
diff --git a/src/test/burn/TestData/TestBA/TestBAWixlib/NetCore6_Platform.wxi b/src/test/burn/TestData/TestBA/TestBAWixlib/NetCore6_Platform.wxi
index 8f084b43..c09c3624 100644
--- a/src/test/burn/TestData/TestBA/TestBAWixlib/NetCore6_Platform.wxi
+++ b/src/test/burn/TestData/TestBA/TestBAWixlib/NetCore6_Platform.wxi
@@ -21,9 +21,10 @@
   <?define DotNetCoreRedistInstallArguments = /install /quiet /log "[$(var.DotNetCoreRedistLog)]"?>
   <?define DotNetCoreRedistUninstallArguments = /uninstall /quiet /log "[$(var.DotNetCoreRedistLog)]"?>
 
-  <?define DotNetCoreSdkId = DOTNETCORERUNTIME6_$(var.NetCoreSdkPlatform)?>
-  <?define DotNetCoreSdkRedistId = DotNetCoreRuntime$(var.NetCoreIdVersion)Redist_$(var.NetCoreSdkPlatform)?>
-  <?define DotNetCoreSdkRedistLog = DotNetCoreRuntime$(var.NetCoreSdkIdVersion)RedistLog?>
+  <?define DotNetCoreSdkId = DOTNETCORESDK6_$(var.NetCorePlatform)?>
+  <?define DotNetCoreSdkFeatureBandId = DOTNETCORESDK$(var.NetCoreSdkFeatureBandVersion)_$(var.NetCorePlatform)?>
+  <?define DotNetCoreSdkRedistId = DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist_$(var.NetCorePlatform)?>
+  <?define DotNetCoreSdkRedistLog = DotNetCoreSdk$(var.NetCoreSdkIdVersion)RedistLog?>
   <?define DotNetCoreSdkRedistInstallArguments = /install /quiet /log "[$(var.DotNetCoreSdkRedistLog)]"?>
   <?define DotNetCoreSdkRedistUninstallArguments = /uninstall /quiet /log "[$(var.DotNetCoreSdkRedistLog)]"?>
 
diff --git a/src/test/burn/TestData/TestBA/TestBAWixlib_x64/NetCore6.0.9_x64.wxs b/src/test/burn/TestData/TestBA/TestBAWixlib_x64/NetCore6.0.9_x64.wxs
index e9ccdc51..0bf4cb35 100644
--- a/src/test/burn/TestData/TestBA/TestBAWixlib_x64/NetCore6.0.9_x64.wxs
+++ b/src/test/burn/TestData/TestBA/TestBAWixlib_x64/NetCore6.0.9_x64.wxs
@@ -5,7 +5,7 @@
     <?define NetCorePlatform = x64?>
     <?define NetCoreIdVersion = 609?>
     <?define NetCoreVersion = 6.0.9?>
-    <?define NetCoreSdkPlatform = x64?>
+    <?define NetCoreSdkFeatureBandVersion = 60400?>
     <?define NetCoreSdkIdVersion = 60403?>
     <?define NetCoreSdkVersion = 6.0.403?>
     <?include ..\TestBAWixlib\NetCore6_Platform.wxi?>
@@ -61,16 +61,21 @@
     </Fragment>
 
     <Fragment>
-        <netfx:DotNetCoreSdkSearch Id="$(var.DotNetCoreSdkId)" Platform="$(var.NetCoreSdkPlatform)" Version="6.0.200" Variable="$(var.DotNetCoreSdkId)" />
+        <netfx:DotNetCoreSdkSearch Id="$(var.DotNetCoreSdkId)" Platform="$(var.NetCorePlatform)" MajorVersion="6" Variable="$(var.DotNetCoreSdkId)" />
+        <netfx:DotNetCoreSdkFeatureBandSearch Id="$(var.DotNetCoreSdkFeatureBandId)" Platform="$(var.NetCorePlatform)" Version="6.0.400" Variable="$(var.DotNetCoreSdkFeatureBandId)" />
+    </Fragment>
+
+    <Fragment>
+        <netfx:DotNetCoreSdkFeatureBandSearchRef Id="$(var.DotNetCoreSdkFeatureBandId)" />
 
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)DetectCondition" Value="$(var.DotNetCoreSdkId) &gt;= v$(var.NetCoreSdkVersion)" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)InstallCondition" Value="" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)PackageDirectory" Value="redist\" Overridable="yes" />
-        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)RepairArguments" Value="" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)DetectCondition" Value="$(var.DotNetCoreSdkFeatureBandId) &gt;= v$(var.NetCoreSdkVersion)" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)InstallCondition" Value="" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)PackageDirectory" Value="redist\" Overridable="yes" />
+        <WixVariable Id="DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)RepairArguments" Value="" Overridable="yes" />
 
         <PackageGroup Id="$(var.DotNetCoreSdkRedistId)">
-            <ExePackage CacheId="$(var.DotNetCoreSdkRedistId)_2485A7AFA98E178CB8F30C9838346B514AEA4769" InstallArguments="$(var.DotNetCoreSdkRedistInstallArguments)" PerMachine="yes" DetectCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)DetectCondition)" InstallCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)InstallCondition)" Id="$(var.DotNetCoreSdkRedistId)" Vital="yes" Permanent="yes" Protocol="burn" LogPathVariable="$(var.DotNetCoreSdkRedistLog)" Cache="remove">
-                <ExePackagePayload Name="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCoreSdkPlatform)PackageDirectory)dotnet-sdk-$(var.NetCoreSdkVersion)-win-$(var.NetCoreSdkPlatform).exe" DownloadUrl="$(var.DotNetCoreSdkRedistLink)" ProductName="Microsoft .NET 6.0 SDK - 6.0.403 (x64)" Description="Microsoft .NET SDK - 6.0.403 (x64)" CertificatePublicKey="3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18" CertificateThumbprint="2485A7AFA98E178CB8F30C9838346B514AEA4769" Size="205722192" Version="6.0.403" />
+            <ExePackage CacheId="$(var.DotNetCoreSdkRedistId)_2485A7AFA98E178CB8F30C9838346B514AEA4769" InstallArguments="$(var.DotNetCoreSdkRedistInstallArguments)" PerMachine="yes" DetectCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)DetectCondition)" InstallCondition="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)InstallCondition)" Id="$(var.DotNetCoreSdkRedistId)" Vital="yes" Permanent="yes" Protocol="burn" LogPathVariable="$(var.DotNetCoreSdkRedistLog)" Cache="remove">
+                <ExePackagePayload Name="!(wix.DotNetCoreSdk$(var.NetCoreSdkIdVersion)Redist$(var.NetCorePlatform)PackageDirectory)dotnet-sdk-$(var.NetCoreSdkVersion)-win-$(var.NetCorePlatform).exe" DownloadUrl="$(var.DotNetCoreSdkRedistLink)" ProductName="Microsoft .NET 6.0 SDK - 6.0.403 (x64)" Description="Microsoft .NET SDK - 6.0.403 (x64)" CertificatePublicKey="3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18" CertificateThumbprint="2485A7AFA98E178CB8F30C9838346B514AEA4769" Size="205722192" Version="6.0.403" />
             </ExePackage>
         </PackageGroup>
     </Fragment>
diff --git a/src/test/burn/TestData/TestBA/TestBAWixlib_x64/WixBA_x64.wxs b/src/test/burn/TestData/TestBA/TestBAWixlib_x64/WixBA_x64.wxs
index 6cfbc937..e9cd9827 100644
--- a/src/test/burn/TestData/TestBA/TestBAWixlib_x64/WixBA_x64.wxs
+++ b/src/test/burn/TestData/TestBA/TestBAWixlib_x64/WixBA_x64.wxs
@@ -1,5 +1,5 @@
-<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
-<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:netfx="http://wixtoolset.org/schemas/v4/wxs/netfx">
   <Fragment>
     <BootstrapperApplication>
       <Payload SourceFile="!(bindpath.dncx64)\WixToolset.WixBA.deps.json" />
@@ -14,5 +14,7 @@
     <PackageGroup Id="WixBAdnc_x64">
       <PackageGroupRef Id="DesktopNetCoreRuntime609Redist_x64AsPrereq" />
     </PackageGroup>
+
+    <netfx:DotNetCoreSdkSearchRef Id="DOTNETCORESDK6_x64" />
   </Fragment>
 </Wix>
-- 
cgit v1.2.3-55-g6feb