Repository: microsoft/terminal (language: cpp)

Issue:
The WPF nuget package isn't signed by our CI
From Teams with @javierdlg 

> We hit a snag with out insertion process and we found out that the Microsoft.Terminal.Wpf package does not contain signed files
> Is there a reason for this? Or is the sign step just not part of the release pipeline?

> I honestly don't know... I bet we just forgot it 😅


Discussion / hints:
This actually is known! VS used to re-sign our binaries before deployment.

I was dismayed to learn that signing the `nupkg` does not sign the inner DLLs. Then again, neither did the `MSIX`.
Not a pressing issue atm, we are currently signing the dlls ourselves but it would be best if they came already signed since we get security warnings about signing 3rd party dlls.

Thanks for looking into this!

Fix patch:
diff --git a/build/Helix/AzurePipelinesHelperScripts.ps1 b/build/Helix/AzurePipelinesHelperScripts.ps1
deleted file mode 100644
index 8934a8548d1..00000000000
--- a/build/Helix/AzurePipelinesHelperScripts.ps1
+++ /dev/null
@@ -1,175 +0,0 @@
-function GetAzureDevOpsBaseUri
-{
-    Param(
-        [string]$CollectionUri,
-        [string]$TeamProject
-    )
-
-    return $CollectionUri + $TeamProject
-}
-
-function GetQueryTestRunsUri
-{
-    Param(
-        [string]$CollectionUri,
-        [string]$TeamProject,
-        [string]$BuildUri,
-        [switch]$IncludeRunDetails
-    )
-
-    if ($IncludeRunDetails)
-    {
-        $includeRunDetailsParameter = "&includeRunDetails=true"
-    }
-    else
-    {
-        $includeRunDetailsParameter = ""
-    }
-
-    $baseUri = GetAzureDevOpsBaseUri -CollectionUri $CollectionUri -TeamProject $TeamProject
-    $queryUri = "$baseUri/_apis/test/runs?buildUri=$BuildUri$includeRunDetailsParameter&api-version=5.0"
-    return $queryUri
-}
-
-function Get-HelixJobTypeFromTestRun
-{
-    Param ($testRun)
-
-    $testRunSingleResultUri = "$($testRun.url)/results?`$top=1&`$skip=0&api-version=5.1"
-    $singleTestResult = Invoke-RestMethod -Uri $testRunSingleResultUri -Method Get -Headers $azureDevOpsRestApiHeaders
-    $count = $singleTestResult.value.Length
-    if($count -eq 0)
-    {
-        # If the count is 0, then results have not yet been reported for this run.
-        # We only care about completed runs with results, so it is ok to just return 'UNKNOWN' for this run.
-        return "UNKNOWN"
-    }
-    else
-    {
-        $info = ConvertFrom-Json $singleTestResult.value.comment
-        $helixJobId = $info.HelixJobId
-        $job = Invoke-RestMethodWithRetries "https://helix.dot.net/api/2019-06-17/jobs/${helixJobId}?access_token=${HelixAccessToken}"
-        return $job.Type
-    }
-}
-
-function Append-HelixAccessTokenToUrl
-{
-    Param ([string]$url, [string]$token)
-    if($url.Contains("?"))
-    {
-        $url = "$($url)&access_token=$($token)"
-    }
-    else
-    {
-        $url = "$($url)?access_token=$($token)"
-    }
-    return $url
-}
-
-
-# The Helix Rest api is sometimes unreliable. So we call these apis with retry logic. 
-# Note: The Azure DevOps apis are stable and do not need to be called with this retry logic.
-$helixApiRetries = 0
-$helixApiRetriesMax = 10 
-
-function Download-StringWithRetries
-{
-    Param ([string]$fileName, [string]$url)
-
-    $result = ""
-    $done = $false
-
-    while(!($done))
-    {
-        try
-        {
-            Write-Host "Downloading $fileName"
-            $result = (New-Object System.Net.WebClient).DownloadString($url)
-            $done = $true
-        }
-        catch
-        {
-            Write-Host "Failed to download $fileName $($PSItem.Exception)"
-
-            $helixApiRetries = $helixApiRetries + 1
-            if($helixApiRetries -lt $helixApiRetriesMax)
-            {
-                Write-Host "Sleep and retry download of $fileName"
-                Start-Sleep 60
-            }
-            else
-            {
-                throw "Failed to download $fileName"
-            }
-        }
-    }
-
-    return $result
-}
-
-function Invoke-RestMethodWithRetries
-{
-    Param ([string]$url,$Headers)
-
-    $result = @()
-    $done = $false
-
-    while(!($done))
-    {
-        try
-        {
-            $result = Invoke-RestMethod -Uri $url -Method Get -Headers $Headers
-            $done = $true
-        }
-        catch
-        {
-            Write-Host "Failed to invoke Rest method $($PSItem.Exception)"
-
-            $helixApiRetries = $helixApiRetries + 1
-            if($helixApiRetries -lt $helixApiRetriesMax)
-            {
-                Write-Host "Sleep and retry invoke"
-                Start-Sleep 60
-            }
-            else
-            {
-                throw "Failed to invoke Rest method"
-            }
-        }
-    }
-
-    return $result
-}
-
-function Download-FileWithRetries
-{
-    Param ([string]$fileurl, [string]$destination)
-
-    $done = $false
-
-    while(!($done))
-    {
-        try
-        {
-            Write-Host "Downloading $destination"
-            $webClient.DownloadFile($fileurl, $destination)
-            $done = $true
-        }
-        catch
-        {
-            Write-Host "Failed to download $destination $($PSItem.Exception)"
-
-            $helixApiRetries = $helixApiRetries + 1
-            if($helixApiRetries -lt $helixApiRetriesMax)
-            {
-                Write-Host "Sleep and retry download of $destination"
-                Start-Sleep 60
-            }
-            else
-            {
-                throw "Failed to download $destination"
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/build/Helix/EnsureMachineState.ps1 b/build/Helix/EnsureMachineState.ps1
deleted file mode 100644
index 6f26da63a56..00000000000
--- a/build/Helix/EnsureMachineState.ps1
+++ /dev/null
@@ -1,112 +0,0 @@
-$scriptDirectory = $script:MyInvocation.MyCommand.Path | Split-Path -Parent
-
-# List all processes to aid debugging:
-Write-Host "All processes running:"
-Get-Process
-
-tasklist /svc
-
-# Add this test directory as an exclusion for Windows Defender
-Write-Host "Add $scriptDirectory as Exclusion Path"
-Add-MpPreference -ExclusionPath $scriptDirectory
-Write-Host "Add $($env:HELIX_CORRELATION_PAYLOAD) as Exclusion Path"
-Add-MpPreference -ExclusionPath $env:HELIX_CORRELATION_PAYLOAD
-Get-MpPreference
-Get-MpComputerStatus
-
-
-# Minimize all windows:
-$shell = New-Object -ComObject "Shell.Application"
-$shell.minimizeall()
-
-# Kill any instances of Windows Security Alert:
-$windowTitleToMatch = "*Windows Security Alert*"
-$procs = Get-Process | Where {$_.MainWindowTitle -like "*Windows Security Alert*"}
-foreach ($proc in $procs)
-{
-    Write-Host "Found process with '$windowTitleToMatch' title: $proc"
-    $proc.Kill();
-}
-
-# Kill processes by name that are known to interfere with our tests:
-$processNamesToStop = @("Microsoft.Photos", "WinStore.App", "SkypeApp", "SkypeBackgroundHost", "OneDriveSetup", "OneDrive")
-foreach($procName in $processNamesToStop)
-{
-    Write-Host "Attempting to kill $procName if it is running"
-    Stop-Process -ProcessName $procName -Verbose -ErrorAction Ignore   
-}
-Write-Host "All processes running after attempting to kill unwanted processes:"
-Get-Process
-
-tasklist /svc
-
-$platform = $env:testbuildplatform
-if(!$platform)
-{
-    $platform = "x86"
-}
-
-function UninstallApps {
-    Param([string[]]$appsToUninstall)
-
-    foreach($pkgName in $appsToUninstall)
-    {
-        foreach($pkg in (Get-AppxPackage $pkgName).PackageFullName) 
-        {
-            Write-Output "Removing: $pkg" 
-            Remove-AppxPackage $pkg
-        } 
-    }
-}
-
-function UninstallTestApps {
-    Param([string[]]$appsToUninstall)
-
-    foreach($pkgName in $appsToUninstall)
-    {
-        foreach($pkg in (Get-AppxPackage $pkgName).PackageFullName) 
-        {
-            Write-Output "Removing: $pkg" 
-            Remove-AppxPackage $pkg
-        }
-
-        # Sometimes an app can get into a state where it is no longer returned by Get-AppxPackage, but it is still present
-        # which prevents other versions of the app from being installed.
-        # To handle this, we can directly call Remove-AppxPackage against the full name of the package. However, without
-        # Get-AppxPackage to find the PackageFullName, we just have to manually construct the name.
-        $packageFullName = "$($pkgName)_1.0.0.0_$($platform)__8wekyb3d8bbwe" 
-        Write-Host "Removing $packageFullName if installed"
-        Remove-AppPackage $packageFullName -ErrorVariable appxerror -ErrorAction SilentlyContinue 
-        if($appxerror)
-        {
-            foreach($error in $appxerror)
-            {
-                # In most cases, Remove-AppPackage will fail due to the package not being found. Don't treat this as an error.
-                if(!($error.Exception.Message -match "0x80073CF1"))
-                {
-                    Write-Error $error
-                }
-            }
-        }
-        else
-        {
-            Write-Host "Successfully removed $packageFullName"
-        }
-    }
-}
-
-Write-Host "Uninstall AppX packages that are known to cause issues with our tests"
-UninstallApps("*Skype*", "*Windows.Photos*")
-
-Write-Host "Uninstall any of our test apps that may have been left over from previous test runs"
-UninstallTestApps("NugetPackageTestApp", "NugetPackageTestAppCX", "IXMPTestApp", "MUXControlsTestApp")
-
-Write-Host "Uninstall MUX Framework package that may have been left over from previous test runs"
-# We don't want to uninstall all versions of the MUX Framework package, as there may be other apps preinstalled on the system 
-# that depend on it. We only uninstall the Framework package that corresponds to the version of MUX that we are testing.
-[xml]$versionData = (Get-Content "version.props")
-$versionMajor = $versionData.GetElementsByTagName("MUXVersionMajor").'#text'
-$versionMinor = $versionData.GetElementsByTagName("MUXVersionMinor").'#text'
-UninstallApps("Microsoft.UI.Xaml.$versionMajor.$versionMinor")
-
-Get-Process
\ No newline at end of file
diff --git a/build/Helix/GenerateTestProjFile.ps1 b/build/Helix/GenerateTestProjFile.ps1
deleted file mode 100644
index 07a3550a79a..00000000000
--- a/build/Helix/GenerateTestProjFile.ps1
+++ /dev/null
@@ -1,336 +0,0 @@
-[CmdLetBinding()]
-Param(
-    [Parameter(Mandatory = $true)] 
-    [string]$TestFile,
-
-    [Parameter(Mandatory = $true)] 
-    [string]$OutputProjFile,
-
-    [Parameter(Mandatory = $true)] 
-    [string]$JobTestSuiteName,
-
-    [Parameter(Mandatory = $true)] 
-    [string]$TaefPath,
-
-    [string]$TaefQuery
-)
-
-Class TestCollection
-{
-    [string]$Name
-    [string]$SetupMethodName
-    [string]$TeardownMethodName
-    [System.Collections.Generic.Dictionary[string, string]]$Properties
-
-    TestCollection()
-    {
-        if ($this.GetType() -eq [TestCollection])
-        {
-            throw "This class should never be instantiated directly; it should only be derived from."
-        }
-    }
-
-    TestCollection([string]$name)
-    {
-        $this.Init($name)
-    }
-
-    hidden Init([string]$name)
-    {
-        $this.Name = $name
-        $this.Properties = @{}
-    }
-}
-
-Class Test : TestCollection 
-{
-    Test([string]$name)
-    {
-        $this.Init($name)
-    }
-}
-
-Class TestClass : TestCollection
-{
-    [System.Collections.Generic.List[Test]]$Tests
-
-    TestClass([string]$name)
-    {
-        $this.Init($name)
-        $this.Tests = @{}
-    }
-}
-
-Class TestModule : TestCollection
-{
-    [System.Collections.Generic.List[TestClass]]$TestClasses
-
-    TestModule([string]$name)
-    {
-        $this.Init($name)
-        $this.TestClasses = @{}
-    }
-}
-
-function Parse-TestInfo([string]$taefOutput)
-{
-    enum LineType
-    {
-        None
-        TestModule
-        TestClass
-        Test
-        Setup
-        Teardown
-        Property
-    }
-        
-    [string]$testModuleIndentation = "        "
-    [string]$testClassIndentation = "            "
-    [string]$testIndentation = "                "
-    [string]$setupBeginning = "Setup: "
-    [string]$teardownBeginning = "Teardown: "
-    [string]$propertyBeginning = "Property["
-
-    function Get-LineType([string]$line)
-    {
-        if ($line.Contains($setupBeginning))
-        {
-            return [LineType]::Setup;
-        }
-        elseif ($line.Contains($teardownBeginning))
-        {
-            return [LineType]::Teardown;
-        }
-        elseif ($line.Contains($propertyBeginning))
-        {
-            return [LineType]::Property;
-        }
-        elseif ($line.StartsWith($testModuleIndentation) -and -not $line.StartsWith("$testModuleIndentation "))
-        {
-            return [LineType]::TestModule;
-        }
-        elseif ($line.StartsWith($testClassIndentation) -and -not $line.StartsWith("$testClassIndentation "))
-        {
-            return [LineType]::TestClass;
-        }
-        elseif ($line.StartsWith($testIndentation) -and -not $line.StartsWith("$testIndentation "))
-        {
-            return [LineType]::Test;
-        }
-        else
-        {
-            return [LineType]::None;
-        }
-    }
-
-    [string[]]$lines = $taefOutput.Split(@([Environment]::NewLine, "`n"), [StringSplitOptions]::RemoveEmptyEntries)
-    [System.Collections.Generic.List[TestModule]]$testModules = @()
-
-    [TestModule]$currentTestModule = $null
-    [TestClass]$currentTestClass = $null
-    [Test]$currentTest = $null
-
-    [TestCollection]$lastTestCollection = $null
-
-    foreach ($rawLine in $lines)
-    {
-        [LineType]$lineType = (Get-LineType $rawLine)
-
-        # We don't need the whitespace around the line anymore, so we'll discard it to make things easier.
-        [string]$line = $rawLine.Trim()
-
-        if ($lineType -eq [LineType]::TestModule)
-        {
-            if ($currentTest -ne $null -and $currentTestClass -ne $null)
-            {
-                $currentTestClass.Tests.Add($currentTest)
-            }
-
-            if ($currentTestClass -ne $null -and $currentTestModule -ne $null)
-            {
-                $currentTestModule.TestClasses.Add($currentTestClass)
-            }
-
-            if ($currentTestModule -ne $null)
-            {
-                $testModules.Add($currentTestModule)
-            }
-
-            $currentTestModule = [TestModule]::new($line)
-            $currentTestClass = $null
-            $currentTest = $null
-            $lastTestCollection = $currentTestModule
-        }
-        elseif ($lineType -eq [LineType]::TestClass)
-        {
-            if ($currentTest -ne $null -and $currentTestClass -ne $null)
-            {
-                $currentTestClass.Tests.Add($currentTest)
-            }
-
-            if ($currentTestClass -ne $null -and $currentTestModule -ne $null)
-            {
-                $currentTestModule.TestClasses.Add($currentTestClass)
-            }
-
-            $currentTestClass = [TestClass]::new($line)
-            $currentTest = $null
-            $lastTestCollection = $currentTestClass
-        }
-        elseif ($lineType -eq [LineType]::Test)
-        {
-            if ($currentTest -ne $null -and $currentTestClass -ne $null)
-            {
-                $currentTestClass.Tests.Add($currentTest)
-            }
-
-            $currentTest = [Test]::new($line)
-            $lastTestCollection = $currentTest
-        }
-        elseif ($lineType -eq [LineType]::Setup)
-        {
-            if ($lastTestCollection -ne $null)
-            {
-                $lastTestCollection.SetupMethodName = $line.Replace($setupBeginning, "")
-            }
-        }
-        elseif ($lineType -eq [LineType]::Teardown)
-        {
-            if ($lastTestCollection -ne $null)
-            {
-                $lastTestCollection.TeardownMethodName = $line.Replace($teardownBeginning, "")
-            }
-        }
-        elseif ($lineType -eq [LineType]::Property)
-        {
-            if ($lastTestCollection -ne $null)
-            {
-                foreach ($match in [Regex]::Matches($line, "Property\[(.*)\]\s+=\s+(.*)"))
-                {
-                    [string]$propertyKey = $match.Groups[1].Value;
-                    [string]$propertyValue = $match.Groups[2].Value;
-                    $lastTestCollection.Properties.Add($propertyKey, $propertyValue);
-                }
-            }
-        }
-    }
-
-    if ($currentTest -ne $null -and $currentTestClass -ne $null)
-    {
-        $currentTestClass.Tests.Add($currentTest)
-    }
-
-    if ($currentTestClass -ne $null -and $currentTestModule -ne $null)
-    {
-        $currentTestModule.TestClasses.Add($currentTestClass)
-    }
-
-    if ($currentTestModule -ne $null)
-    {
-        $testModules.Add($currentTestModule)
-    }
-
-    return $testModules
-}
-
-Write-Verbose "TaefQuery = $TaefQuery"
-
-$TaefSelectQuery = ""
-$TaefQueryToAppend = ""
-if($TaefQuery)
-{
-    $TaefSelectQuery = "/select:`"$TaefQuery`""
-    $TaefQueryToAppend = " and $TaefQuery"
-}
-Write-Verbose "TaefSelectQuery = $TaefSelectQuery"
-
-
-$taefExe = "$TaefPath\te.exe"
-[string]$taefOutput = & "$taefExe" /listproperties $TaefSelectQuery $TestFile | Out-String
-
-[System.Collections.Generic.List[TestModule]]$testModules = (Parse-TestInfo $taefOutput)
-
-$projFileContent = @"
-<Project>
-  <ItemGroup>
-"@
-
-foreach ($testModule in $testModules)
-{
-    foreach ($testClass in $testModules.TestClasses)
-    {
-        Write-Host "Generating Helix work item for test class $($testClass.Name)..."
-        [System.Collections.Generic.List[string]]$testSuiteNames = @()
-        
-        $testSuiteExists = $false
-        $suitelessTestExists = $false
-
-        foreach ($test in $testClass.Tests)
-        {
-            # A test method inherits its 'TestSuite' property from its TestClass
-            if (!$test.Properties.ContainsKey("TestSuite") -and $testClass.Properties.ContainsKey("TestSuite"))
-            {
-                $test.Properties["TestSuite"] = $testClass.Properties["TestSuite"]
-            }
-
-            if ($test.Properties.ContainsKey("TestSuite"))
-            {
-                [string]$testSuite = $test.Properties["TestSuite"]
-
-                if (-not $testSuiteNames.Contains($testSuite))
-                {
-                    Write-Host "    Found test suite $testSuite. Generating Helix work item for it as well."
-                    $testSuiteNames.Add($testSuite)
-                }
-
-                $testSuiteExists = $true
-            }
-            else
-            {
-                $suitelessTestExists = $true
-            }
-        }
-        
-        $testClassSelectPattern = "$($testClass.Name).*"
-        if($testClass.Name.Contains("::"))
-        {
-            $testClassSelectPattern = "$($testClass.Name)::*"
-        }
-        $testNameQuery= "(@Name='$testClassSelectPattern')"
-
-        $workItemName = $testClass.Name
-        # Native tests use '::' as a separator, which is not valid for workItem names.
-        $workItemName = $workItemName -replace "::", "-"
-        
-        if ($suitelessTestExists)
-        {
-            $projFileContent += @"
-
-    <HelixWorkItem Include="$($workItemName)" Condition="'`$(TestSuite)'=='$($JobTestSuiteName)'">
-      <Timeout>00:30:00</Timeout>
-      <Command>call %HELIX_CORRELATION_PAYLOAD%\runtests.cmd /select:"(@Name='$($testClass.Name)*'$(if ($testSuiteExists) { "and not @TestSuite='*'" }))$($TaefQueryToAppend)"</Command>
-    </HelixWorkItem>
-"@
-        }
-        
-        foreach ($testSuiteName in $testSuiteNames)
-        {
-            $projFileContent += @"
-
-    <HelixWorkItem Include="$($workItemName)-$testSuiteName" Condition="'`$(TestSuite)'=='$($JobTestSuiteName)'">
-      <Timeout>00:30:00</Timeout>
-      <Command>call %HELIX_CORRELATION_PAYLOAD%\runtests.cmd /select:"(@Name='$($testClass.Name)*' and @TestSuite='$testSuiteName')$($TaefQueryToAppend)"</Command>
-    </HelixWorkItem>
-"@
-        }
-    }
-}
-
-$projFileContent += @"
-
-  </ItemGroup>
-</Project>
-"@
-
-Set-Content $OutputProjFile $projFileContent -NoNewline -Encoding UTF8
\ No newline at end of file
diff --git a/build/Helix/InstallTestAppDependencies.ps1 b/build/Helix/InstallTestAppDependencies.ps1
deleted file mode 100644
index da0a049d27d..00000000000
--- a/build/Helix/InstallTestAppDependencies.ps1
+++ /dev/null
@@ -1,12 +0,0 @@
-# Displaying progress is unnecessary and is just distracting.
-$ProgressPreference = "SilentlyContinue"
-
-$dependencyFiles = Get-ChildItem -Filter "*Microsoft.VCLibs.*.appx"
-
-foreach ($file in $dependencyFiles)
-{
-    Write-Host "Adding dependency $($file)..."
-
-    Add-AppxPackage $file
-    
-}
\ No newline at end of file
diff --git a/build/Helix/OutputFailedTestQuery.ps1 b/build/Helix/OutputFailedTestQuery.ps1
deleted file mode 100644
index 3ea7abf80ad..00000000000
--- a/build/Helix/OutputFailedTestQuery.ps1
+++ /dev/null
@@ -1,8 +0,0 @@
-Param(
-    [Parameter(Mandatory = $true)] 
-    [string]$WttInputPath
-)
-
-Add-Type -Language CSharp -ReferencedAssemblies System.Xml,System.Xml.Linq,System.Runtime.Serialization,System.Runtime.Serialization.Json (Get-Content $PSScriptRoot\HelixTestHelpers.cs -Raw)
-
-[HelixTestHelpers.FailedTestDetector]::OutputFailedTestQuery($WttInputPath)
\ No newline at end of file
diff --git a/build/Helix/OutputSubResultsJsonFiles.ps1 b/build/Helix/OutputSubResultsJsonFiles.ps1
deleted file mode 100644
index 476502e9878..00000000000
--- a/build/Helix/OutputSubResultsJsonFiles.ps1
+++ /dev/null
@@ -1,32 +0,0 @@
-Param(
-    [Parameter(Mandatory = $true)] 
-    [string]$WttInputPath,
-
-    [Parameter(Mandatory = $true)] 
-    [string]$WttSingleRerunInputPath,
-
-    [Parameter(Mandatory = $true)] 
-    [string]$WttMultipleRerunInputPath,
-
-    [Parameter(Mandatory = $true)] 
-    [string]$TestNamePrefix
-)
-
-# Ideally these would be passed as parameters to the script. However ps makes it difficult to deal with string literals containing '&', so we just 
-# read the values directly from the environment variables
-$helixResultsContainerUri = $Env:HELIX_RESULTS_CONTAINER_URI
-$helixResultsContainerRsas = $Env:HELIX_RESULTS_CONTAINER_RSAS
-
-Add-Type -Language CSharp -ReferencedAssemblies System.Xml,System.Xml.Linq,System.Runtime.Serialization,System.Runtime.Serialization.Json (Get-Content $PSScriptRoot\HelixTestHelpers.cs -Raw)
-
-$testResultParser = [HelixTestHelpers.TestResultParser]::new($TestNamePrefix, $helixResultsContainerUri, $helixResultsContainerRsas)
-[System.Collections.Generic.Dictionary[string, string]]$subResultsJsonByMethodName = $testResultParser.GetSubResultsJsonByMethodName($WttInputPath, $WttSingleRerunInputPath, $WttMultipleRerunInputPath)
-
-$subResultsJsonDirectory = [System.IO.Path]::GetDirectoryName($WttInputPath)
-
-foreach ($methodName in $subResultsJsonByMethodName.Keys)
-{
-    $subResultsJson = $subResultsJsonByMethodName[$methodName]
-    $subResultsJsonPath = [System.IO.Path]::Combine($subResultsJsonDirectory, $methodName + "_subresults.json")
-    Out-File $subResultsJsonPath -Encoding utf8 -InputObject $subResultsJson
-}
\ No newline at end of file
diff --git a/build/Helix/OutputTestResults.ps1 b/build/Helix/OutputTestResults.ps1
deleted file mode 100644
index a23b456afe8..00000000000
--- a/build/Helix/OutputTestResults.ps1
+++ /dev/null
@@ -1,131 +0,0 @@
-Param(
-    [Parameter(Mandatory = $true)] 
-    [int]$MinimumExpectedTestsExecutedCount,
-
-    [string]$AccessToken = $env:SYSTEM_ACCESSTOKEN,
-    [string]$CollectionUri = $env:SYSTEM_COLLECTIONURI,
-    [string]$TeamProject = $env:SYSTEM_TEAMPROJECT,
-    [string]$BuildUri = $env:BUILD_BUILDURI,
-    [bool]$CheckJobAttempt
-)
-
-$azureDevOpsRestApiHeaders = @{
-    "Accept"="application/json"
-    "Authorization"="Basic $([System.Convert]::ToBase64String([System.Text.ASCIIEncoding]::ASCII.GetBytes(":$AccessToken")))"
-}
-
-. "$PSScriptRoot/AzurePipelinesHelperScripts.ps1"
-
-Write-Host "Checking test results..."
-
-$queryUri = GetQueryTestRunsUri -CollectionUri $CollectionUri -TeamProject $TeamProject -BuildUri $BuildUri -IncludeRunDetails
-Write-Host "queryUri = $queryUri"
-
-$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
-[System.Collections.Generic.List[string]]$failingTests = @()
-[System.Collections.Generic.List[string]]$unreliableTests = @()
-[System.Collections.Generic.List[string]]$unexpectedResultTest = @()
-
-[System.Collections.Generic.List[string]]$namesOfProcessedTestRuns = @()
-$totalTestsExecutedCount = 0
-
-# We assume that we only have one testRun with a given name that we care about
-# We only process the last testRun with a given name (based on completedDate)
-# The name of a testRun is set to the Helix queue that it was run on (e.g. windows.10.amd64.client21h1.xaml)
-# If we have multiple test runs on the same queue that we care about, we will need to re-visit this logic
-foreach ($testRun in ($testRuns.value | Sort-Object -Property "completedDate" -Descending))
-{
-    if ($CheckJobAttempt)
-    {
-        if ($namesOfProcessedTestRuns -contains $testRun.name)
-        {
-            Write-Host "Skipping test run '$($testRun.name)', since we have already processed a test run of that name."
-            continue
-        }
-    }
-    
-    Write-Host "Processing results from test run '$($testRun.name)'"
-    $namesOfProcessedTestRuns.Add($testRun.name)
-
-    $totalTestsExecutedCount += $testRun.totalTests
-
-    $testRunResultsUri = "$($testRun.url)/results?api-version=5.0"
-    $testResults = Invoke-RestMethodWithRetries "$($testRun.url)/results?api-version=5.0" -Headers $azureDevOpsRestApiHeaders
-        
-    foreach ($testResult in $testResults.value)
-    {
-        $shortTestCaseTitle = $testResult.testCaseTitle -replace "[a-zA-Z0-9]+.[a-zA-Z0-9]+.Windows.UI.Xaml.Tests.MUXControls.",""
-
-        if ($testResult.outcome -eq "Failed")
-        {
-            if (-not $failingTests.Contains($shortTestCaseTitle))
-            {
-                $failingTests.Add($shortTestCaseTitle)
-            }
-        }
-        elseif ($testResult.outcome -eq "Warning")
-        {
-            if (-not $unreliableTests.Contains($shortTestCaseTitle))
-            {
-                $unreliableTests.Add($shortTestCaseTitle)
-            }
-        }
-        elseif ($testResult.outcome -ne "Passed")
-        {
-            # We should only see tests with result "Passed", "Failed" or "Warning"
-            if (-not $unexpectedResultTest.Contains($shortTestCaseTitle))
-            {
-                $unexpectedResultTest.Add($shortTestCaseTitle)
-            }
-        }
-    }
-}
-
-if ($unreliableTests.Count -gt 0)
-{
-    Write-Host @"
-##vso[task.logissue type=warning;]Unreliable tests:
-##vso[task.logissue type=warning;]$($unreliableTests -join "$([Environment]::NewLine)##vso[task.logissue type=warning;]")
-
-"@
-}
-
-if ($failingTests.Count -gt 0)
-{
-    Write-Host @"
-##vso[task.logissue type=error;]Failing tests:
-##vso[task.logissue type=error;]$($failingTests -join "$([Environment]::NewLine)##vso[task.logissue type=error;]")
-
-"@
-}
-
-if ($unexpectedResultTest.Count -gt 0)
-{
-    Write-Host @"
-##vso[task.logissue type=error;]Tests with unexpected results:
-##vso[task.logissue type=error;]$($unexpectedResultTest -join "$([Environment]::NewLine)##vso[task.logissue type=error;]")
-
-"@
-}
-
-if($totalTestsExecutedCount -lt $MinimumExpectedTestsExecutedCount)
-{
-    Write-Host "Expected at least $MinimumExpectedTestsExecutedCount tests to be executed."
-    Write-Host "Actual executed test count is: $totalTestsExecutedCount"
-    Write-Host "##vso[task.complete result=Failed;]"
-}
-elseif ($failingTests.Count -gt 0)
-{
-    Write-Host "At least one test failed."
-    Write-Host "##vso[task.complete result=Failed;]"
-}
-elseif ($unreliableTests.Count -gt 0)
-{
-    Write-Host "All tests eventually passed, but some initially failed."
-    Write-Host "##vso[task.complete result=Succeeded;]"
-}
-else
-{
-    Write-Host "All tests passed."
-    Write-Host "##vso[task.complete result=Succeeded;]"
-}
diff --git a/build/Helix/PrepareHelixPayload.ps1 b/build/Helix/PrepareHelixPayload.ps1
deleted file mode 100644
index 42ba9c6f77b..00000000000
--- a/build/Helix/PrepareHelixPayload.ps1
+++ /dev/null
@@ -1,62 +0,0 @@
-[CmdLetBinding()]
-Param(
-    [string]$Platform,
-    [string]$Configuration,
-    [string]$ArtifactName='drop'
-)
-
-$payloadDir = "HelixPayload\$Configuration\$Platform"
-
-$repoDirectory = Join-Path (Split-Path -Parent $script:MyInvocation.MyCommand.Path) "..\..\"
-$nugetPackagesDir = Join-Path (Split-Path -Parent $script:MyInvocation.MyCommand.Path) "packages"
-
-# Create the payload directory. Remove it if it already exists.
-If(test-path $payloadDir)
-{
-    Remove-Item $payloadDir -Recurse
-}
-New-Item -ItemType Directory -Force -Path $payloadDir
-
-# Copy files from nuget packages
-Copy-Item "$nugetPackagesDir\microsoft.windows.apps.test.1.0.181203002\lib\netcoreapp2.1\*.dll" $payloadDir
-Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.60.210621002\build\Binaries\$Platform\*" $payloadDir
-Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.60.210621002\build\Binaries\$Platform\NetFx4.5\*" $payloadDir
-New-Item -ItemType Directory -Force -Path "$payloadDir\.NETCoreApp2.1\"
-Copy-Item "$nugetPackagesDir\runtime.win-$Platform.microsoft.netcore.app.2.1.0\runtimes\win-$Platform\lib\netcoreapp2.1\*" "$payloadDir\.NETCoreApp2.1\"
-Copy-Item "$nugetPackagesDir\runtime.win-$Platform.microsoft.netcore.app.2.1.0\runtimes\win-$Platform\native\*" "$payloadDir\.NETCoreApp2.1\"
-New-Item -ItemType Directory -Force -Path "$payloadDir\content\"
-Copy-Item "$nugetPackagesDir\Microsoft.Internal.Windows.Terminal.TestContent.1.0.1\content\*" "$payloadDir\content\"
-
-function Copy-If-Exists
-{
-    Param($source, $destinationDir)
-
-    if (Test-Path $source)
-    {
-        Write-Host "Copy from '$source' to '$destinationDir'"
-        Copy-Item -Force $source $destinationDir
-    }
-    else
-    {
-        Write-Host "'$source' does not exist."
-    }
-}
-
-# Copy files from the 'drop' artifact dir
-Copy-Item "$repoDirectory\Artifacts\$ArtifactName\$Configuration\$Platform\Test\*" $payloadDir -Recurse
-
-# Copy files from the repo
-New-Item -ItemType Directory -Force -Path "$payloadDir"
-Copy-Item "build\helix\ConvertWttLogToXUnit.ps1" "$payloadDir"
-Copy-Item "build\helix\OutputFailedTestQuery.ps1" "$payloadDir"
-Copy-Item "build\helix\OutputSubResultsJsonFiles.ps1" "$payloadDir"
-Copy-Item "build\helix\HelixTestHelpers.cs" "$payloadDir"
-Copy-Item "build\helix\runtests.cmd" $payloadDir
-Copy-Item "build\helix\InstallTestAppDependencies.ps1" "$payloadDir"
-Copy-Item "build\Helix\EnsureMachineState.ps1" "$payloadDir"
-
-# Extract the unpackaged distribution of Windows Terminal to the payload directory,
-# where it will create a subdirectory named terminal-0.0.1.0
-# This is referenced in TerminalApp.cs later as part of the test harness.
-& tar -x -v -f "$repoDirectory\Artifacts\$ArtifactName\unpackaged\WindowsTerminalDev_0.0.1.0_x64.zip" -C "$payloadDir"
-Copy-Item "res\fonts\*.ttf" "$payloadDir\terminal-0.0.1.0"
diff --git a/build/Helix/ProcessHelixFiles.ps1 b/build/Helix/ProcessHelixFiles.ps1
deleted file mode 100644
index ae9f7582812..00000000000
--- a/build/Helix/ProcessHelixFiles.ps1
+++ /dev/null
@@ -1,130 +0,0 @@
-Param(
-    [string]$AccessToken = $env:SYSTEM_ACCESSTOKEN,
-    [string]$HelixAccessToken = $env:HelixAccessToken,
-    [string]$CollectionUri = $env:SYSTEM_COLLECTIONURI,
-    [string]$TeamProject = $env:SYSTEM_TEAMPROJECT,
-    [string]$BuildUri = $env:BUILD_BUILDURI,
-    [string]$OutputFolder = "HelixOutput"
-)
-
-$helixLinkFile = "$OutputFolder\LinksToHelixTestFiles.html"
-
-
-function Generate-File-Links
-{
-    Param ([Array[]]$files,[string]$sectionName)
-    if($files.Count -gt 0)
-    {
-        Out-File -FilePath $helixLinkFile -Append -InputObject "<div class=$sectionName>"
-        Out-File -FilePath $helixLinkFile -Append -InputObject "<h4>$sectionName</h4>"
-        Out-File -FilePath $helixLinkFile -Append -InputObject "<ul>"
-        foreach($file in $files)
-        {
-            $url = Append-HelixAccessTokenToUrl $file.Link "{Your-Helix-Access-Token-Here}"
-            Out-File -FilePath $helixLinkFile -Append -InputObject "<li>$($url)</li>"
-        }
-        Out-File -FilePath $helixLinkFile -Append -InputObject "</ul>"
-        Out-File -FilePath $helixLinkFile -Append -InputObject "</div>"
-    }
-}
-
-function Append-HelixAccessTokenToUrl
-{
-    Param ([string]$url, [string]$token)
-    if($token)
-    {
-        if($url.Contains("?"))
-        {
-            $url = "$($url)&access_token=$($token)"
-        }
-        else
-        {
-            $url = "$($url)?access_token=$($token)"
-        }
-    }
-    return $url
-}
-
-#Create output directory
-New-Item $OutputFolder -ItemType Directory
-
-$azureDevOpsRestApiHeaders = @{
-    "Accept"="application/json"
-    "Authorization"="Basic $([System.Convert]::ToBase64String([System.Text.ASCIIEncoding]::ASCII.GetBytes(":$AccessToken")))"
-}
-
-. "$PSScriptRoot/AzurePipelinesHelperScripts.ps1"
-
-$queryUri = GetQueryTestRunsUri -CollectionUri $CollectionUri -TeamProject $TeamProject -BuildUri $BuildUri -IncludeRunDetails
-Write-Host "queryUri = $queryUri"
-
-$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
-$webClient = New-Object System.Net.WebClient
-[System.Collections.Generic.List[string]]$workItems = @()
-
-foreach ($testRun in $testRuns.value)
-{
-    Write-Host "testRunUri = $testRun.url"
-    $testResults = Invoke-RestMethodWithRetries "$($testRun.url)/results?api-version=5.0" -Headers $azureDevOpsRestApiHeaders
-    $isTestRunNameShown = $false
-
-    foreach ($testResult in $testResults.value)
-    {
-        $info = ConvertFrom-Json ([System.Web.HttpUtility]::HtmlDecode($testResult.comment))
-        $helixJobId = $info.HelixJobId
-        $helixWorkItemName = $info.HelixWorkItemName
-
-        $workItem = "$helixJobId-$helixWorkItemName"
-
-        Write-Host "Helix Work Item = $workItem"
-
-        if (-not $workItems.Contains($workItem))
-        {
-            $workItems.Add($workItem)
-            $filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files"
-            $filesQueryUri = Append-HelixAccessTokenToUrl $filesQueryUri $helixAccessToken
-            $files = Invoke-RestMethodWithRetries $filesQueryUri
-
-            $screenShots = $files | where { $_.Name.EndsWith(".jpg") }
-            $dumps = $files | where { $_.Name.EndsWith(".dmp") }
-            $pgcFiles = $files | where { $_.Name.EndsWith(".pgc") }
-            if ($screenShots.Count + $dumps.Count + $pgcFiles.Count -gt 0)
-            {
-                if(-Not $isTestRunNameShown)
-                {
-                    Out-File -FilePath $helixLinkFile -Append -InputObject "<h2>$($testRun.name)</h2>"
-                    $isTestRunNameShown = $true
-                }
-                Out-File -FilePath $helixLinkFile -Append -InputObject "<h3>$helixWorkItemName</h3>"
-                Generate-File-Links $screenShots "Screenshots"
-                Generate-File-Links $dumps "CrashDumps"
-                Generate-File-Links $pgcFiles "PGC files"
-                $misc = $files | where { ($screenShots -NotContains $_) -And ($dumps -NotContains $_) -And ($visualTreeVerificationFiles -NotContains $_) -And ($pgcFiles -NotContains $_) }
-                Generate-File-Links $misc "Misc"
-
-                foreach($pgcFile in $pgcFiles)
-                {
-                    $flavorPath = $testResult.automatedTestName.Split('.')[0]
-                    $archPath = $testResult.automatedTestName.Split('.')[1]
-                    $fileName = $pgcFile.Name
-                    $fullPath = "$OutputFolder\PGO\$flavorPath\$archPath"
-                    $destination = "$fullPath\$fileName"
-
-                    Write-Host "Copying $($pgcFile.Name) to $destination"
-
-                    if (-Not (Test-Path $fullPath))
-                    {
-                        New-Item $fullPath -ItemType Directory
-                    }
-
-                    $link = $pgcFile.Link
-
-                    Write-Host "Downloading $link to $destination"
-
-                    $link = Append-HelixAccessTokenToUrl $link $HelixAccessToken
-                    Download-FileWithRetries $link $destination
-                }
-            }
-        }
-    }
-}
diff --git a/build/Helix/RunTestsInHelix.proj b/build/Helix/RunTestsInHelix.proj
deleted file mode 100644
index db00314ff06..00000000000
--- a/build/Helix/RunTestsInHelix.proj
+++ /dev/null
@@ -1,20 +0,0 @@
-<Project Sdk="Microsoft.DotNet.Helix.Sdk" DefaultTargets="Test">
-  <PropertyGroup>
-    <HelixSource>pr/terminal/$(BUILD_SOURCEBRANCH)/</HelixSource>
-    <EnableXUnitReporter>true</EnableXUnitReporter>
-    <EnableAzurePipelinesReporter>true</EnableAzurePipelinesReporter>
-    <FailOnMissionControlTestFailure>true</FailOnMissionControlTestFailure>
-    <HelixPreCommands>$(HelixPreCommands);set testnameprefix=$(Configuration).$(Platform);set testbuildplatform=$(Platform);set rerunPassesRequiredToAvoidFailure=$(rerunPassesRequiredToAvoidFailure)</HelixPreCommands>
-    <OutputPath>..\..\bin\$(Platform)\$(Configuration)\</OutputPath>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <HelixCorrelationPayload Include="..\..\HelixPayload\$(Configuration)\$(Platform)" />
-  </ItemGroup>
-  
-  <!-- These .proj files are generated by the build machine prior to running tests via GenerateTestProjFile.ps1. -->
-  <Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-TerminalAppLocalTests.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
-  <Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-SettingsModelLocalTests.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
-  <Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-HostTestsUIA.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
-  <Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-WindowsTerminalUIATests.proj" Condition=" '$(TestSuite)'=='PgoInstrumentationSuite' " />
-</Project>
\ No newline at end of file
diff --git a/build/Helix/UpdateUnreliableTests.ps1 b/build/Helix/UpdateUnreliableTests.ps1
deleted file mode 100644
index ecf4e8bf7a4..00000000000
--- a/build/Helix/UpdateUnreliableTests.ps1
+++ /dev/null
@@ -1,136 +0,0 @@
-[CmdLetBinding()]
-Param(
-    [Parameter(Mandatory = $true)] 
-    [int]$RerunPassesRequiredToAvoidFailure,
-
-    [string]$AccessToken = $env:SYSTEM_ACCESSTOKEN,
-    [string]$CollectionUri = $env:SYSTEM_COLLECTIONURI,
-    [string]$TeamProject = $env:SYSTEM_TEAMPROJECT,
-    [string]$BuildUri = $env:BUILD_BUILDURI
-)
-
-. "$PSScriptRoot/AzurePipelinesHelperScripts.ps1"
-
-
-$azureDevOpsRestApiHeaders = @{
-    "Accept"="application/json"
-    "Authorization"="Basic $([System.Convert]::ToBase64String([System.Text.ASCIIEncoding]::ASCII.GetBytes(":$AccessToken")))"
-}
-
-$queryUri = GetQueryTestRunsUri -CollectionUri $CollectionUri -TeamProject $TeamProject -BuildUri $BuildUri
-Write-Host "queryUri = $queryUri"
-
-# To account for unreliable tests, we'll iterate through all of the tests associated with this build, check to see any tests that were unreliable
-# (denoted by being marked as "skipped"), and if so, we'll instead mark those tests with a warning and enumerate all of the attempted runs
-# with their pass/fail states as well as any relevant error messages for failed attempts.
-$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
-
-$timesSeenByRunName = @{}
-      
-foreach ($testRun in $testRuns.value)
-{
-    $testRunResultsUri = "$($testRun.url)/results?api-version=5.0"
-        
-    Write-Host "Marking test run `"$($testRun.name)`" as in progress so we can change its results to account for unreliable tests."
-    Invoke-RestMethod "$($testRun.url)?api-version=5.0" -Method Patch -Body (ConvertTo-Json @{ "state" = "InProgress" }) -Headers $azureDevOpsRestApiHeaders -ContentType "application/json" | Out-Null
-
-    Write-Host "Retrieving test results..."
-    $testResults = Invoke-RestMethodWithRetries $testRunResultsUri -Headers $azureDevOpsRestApiHeaders
-        
-    foreach ($testResult in $testResults.value)
-    {
-        $testNeedsSubResultProcessing = $false
-        if ($testResult.outcome -eq "NotExecuted")
-        {
-            $testNeedsSubResultProcessing = $true
-        }
-        elseif($testResult.outcome -eq "Failed")
-        {
-            $testNeedsSubResultProcessing = $testResult.errorMessage -like "*_subresults.json*"
-        }
-
-        if ($testNeedsSubResultProcessing)
-        {
-            Write-Host "  Test $($testResult.testCaseTitle) was detected as unreliable. Updating..."
-            
-            # The errorMessage field contains a link to the JSON-encoded rerun result data.
-            $resultsJson = Download-StringWithRetries "Error results" $testResult.errorMessage
-            $rerunResults = ConvertFrom-Json $resultsJson
-            [System.Collections.Generic.List[System.Collections.Hashtable]]$rerunDataList = @()
-            $attemptCount = 0
-            $passCount = 0
-            $totalDuration = 0
-            
-            foreach ($rerun in $rerunResults.results)
-            {
-                $rerunData = @{
-                    "displayName" = "Attempt #$($attemptCount + 1) - $($testResult.testCaseTitle)";
-                    "durationInMs" = $rerun.duration;
-                    "outcome" = $rerun.outcome;
-                }
-
-                if ($rerun.outcome -eq "Passed")
-                {
-                    $passCount++
-                }
-                
-                if ($attemptCount -gt 0)
-                {
-                    $rerunData["sequenceId"] = $attemptCount
-                }
-
-                Write-Host "    Attempt #$($attemptCount + 1): $($rerun.outcome)"
-                
-                if ($rerun.outcome -ne "Passed")
-                {
-                    $screenshots = "$($rerunResults.blobPrefix)/$($rerun.screenshots -join @"
-$($rerunResults.blobSuffix)
-$($rerunResults.blobPrefix)
-"@)$($rerunResults.blobSuffix)"
-
-                    # We subtract 1 from the error index because we added 1 so we could use 0
-                    # as a default value not injected into the JSON in order to keep its size down.
-                    # We did this because there's a maximum size enforced for the errorMessage parameter
-                    # in the Azure DevOps REST API.
-                    $fullErrorMessage = @"
-Log: $($rerunResults.blobPrefix)/$($rerun.log)$($rerunResults.blobSuffix)
-
-Screenshots:
-$screenshots
-
-Error log:
-$($rerunResults.errors[$rerun.errorIndex - 1])
-"@
-
-                    $rerunData["errorMessage"] = $fullErrorMessage
-                }
-                
-                $attemptCount++
-                $totalDuration += $rerun.duration
-                $rerunDataList.Add($rerunData)
-            }
-
-            $overallOutcome = "Warning"
-            
-            if ($attemptCount -eq 2)
-            {
-                Write-Host "  Test $($testResult.testCaseTitle) passed on the immediate rerun, so we'll mark it as unreliable."
-            }
-            elseif ($passCount -gt $RerunPassesRequiredToAvoidFailure)
-            {
-                Write-Host "  Test $($testResult.testCaseTitle) passed on $passCount of $attemptCount attempts, which is greater than or equal to the $RerunPassesRequiredToAvoidFailure passes required to avoid being marked as failed. Marking as unreliable."
-            }
-            else
-            {
-                Write-Host "  Test $($testResult.testCaseTitle) passed on only $passCount of $attemptCount attempts, which is less than the $RerunPassesRequiredToAvoidFailure passes required to avoid being marked as failed. Marking as failed."
-                $overallOutcome = "Failed"
-            }
-            
-            $updateBody = ConvertTo-Json @(@{ "id" = $testResult.id; "outcome" = $overallOutcome; "errorMessage" = " "; "durationInMs" = $totalDuration; "subResults" = $rerunDataList; "resultGroupType" = "rerun" }) -Depth 5
-            Invoke-RestMethod -Uri $testRunResultsUri -Method Patch -Headers $azureDevOpsRestApiHeaders -Body $updateBody -ContentType "application/json" | Out-Null
-        }
-    }
-        
-    Write-Host "Finished updates. Re-marking test run `"$($testRun.name)`" as completed."
-    Invoke-RestMethod -Uri "$($testRun.url)?api-version=5.0" -Method Patch -Body (ConvertTo-Json @{ "state" = "Completed" }) -Headers $azureDevOpsRestApiHeaders -ContentType "application/json" | Out-Null
-}
diff --git a/build/Helix/global.json b/build/Helix/global.json
deleted file mode 100644
index 53d8ed33c45..00000000000
--- a/build/Helix/global.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "msbuild-sdks": {
-    "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.22525.5"
-  }
-}
diff --git a/build/Helix/packages.config b/build/Helix/packages.config
deleted file mode 100644
index b101a50d6fe..00000000000
--- a/build/Helix/packages.config
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
-  <package id="Microsoft.Internal.Windows.Terminal.TestContent" version="1.0.1" />
-  <package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
-  <package id="microsoft.windows.apps.test" version="1.0.181203002" targetFramework="native" />
-  <package id="runtime.win-x86.microsoft.netcore.app" version="2.1.0" targetFramework="native" />
-  <package id="runtime.win-x64.microsoft.netcore.app" version="2.1.0" targetFramework="native" />
-</packages>
diff --git a/build/Helix/readme.md b/build/Helix/readme.md
deleted file mode 100644
index d51fc973c1e..00000000000
--- a/build/Helix/readme.md
+++ /dev/null
@@ -1,32 +0,0 @@
-This directory contains code and configuration files to run WinUI tests in Helix.
-
-Helix is a cloud hosted test execution environment which is accessed via the Arcade SDK.
-More details:
-* [Arcade](https://github.com/dotnet/arcade)
-* [Helix](https://github.com/dotnet/arcade/tree/master/src/Microsoft.DotNet.Helix/Sdk)
-
-WinUI tests are scheduled in Helix by the Azure DevOps Pipeline: [RunHelixTests.yml](../RunHelixTests.yml).
-
-The workflow is as follows:
-1. NuGet Restore is called on the packages.config in this directory. This downloads any runtime dependencies 
-that are needed to run tests.
-2. PrepareHelixPayload.ps1 is called. This copies the necessary files from various locations into a Helix 
-payload directory. This directory is what will get sent to the Helix machines.
-3. RunTestsInHelix.proj is executed. This proj has a dependency on 
-[Microsoft.DotNet.Helix.Sdk](https://github.com/dotnet/arcade/tree/master/src/Microsoft.DotNet.Helix/Sdk) 
-which it uses to publish the Helix payload directory and to schedule the Helix Work Items. The WinUI tests 
-are parallelized into multiple Helix Work Items.
-4. Each Helix Work Item calls [runtests.cmd](runtests.cmd) with a specific query to pass to 
-[TAEF](https://docs.microsoft.com/en-us/windows-hardware/drivers/taef/) which runs the tests.
-5. If a test is detected to have failed, we run it again, first once, then eight more times if it fails again.
-If it fails all ten times, we report the test as failed; otherwise, we report it as unreliable,
-which will show up as a warning, but which will not fail the build.  When a test is reported as unreliable,
-we include the results for each individual run via a JSON string in the original test's errorMessage field.
-6. TAEF produces logs in WTT format. Helix is able to process logs in XUnit format. We run 
-[ConvertWttLogToXUnit.ps1](ConvertWttLogToXUnit.ps1) to convert the logs into the necessary format.
-7. RunTestsInHelix.proj has EnableAzurePipelinesReporter set to true. This allows the XUnit formatted test 
-results to be reported back to the Azure DevOps Pipeline.
-8. We process unreliable tests once all tests have been reported by reading the JSON string from the
-errorMessage field and calling the Azure DevOps REST API to modify the unreliable tests to have sub-results
-added to the test and to mark the test as "warning", which will enable people to see exactly how the test
-failed in runs where it did.
\ No newline at end of file
diff --git a/build/config/ESRPSigning_ConPTY.json b/build/config/ESRPSigning_ConPTY.json
deleted file mode 100644
index d01e9c9eabe..00000000000
--- a/build/config/ESRPSigning_ConPTY.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
-    "Version": "1.0.0",
-    "UseMinimatch": false,
-    "SignBatches": [
-        {
-            "MatchedPath": [
-                "conpty.dll",
-                "OpenConsole.exe"
-            ],
-            "SigningInfo": {
-                "Operations": [
-                    {
-                        "KeyCode": "CP-230012",
-                        "OperationSetCode": "SigntoolSign",
-                        "Parameters": [
-                            {
-                                "parameterName": "OpusName",
-                                "parameterValue": "Microsoft"
-                            },
-                            {
-                                "parameterName": "OpusInfo",
-                                "parameterValue": "http://www.microsoft.com"
-                            },
-                            {
-                                "parameterName": "FileDigest",
-                                "parameterValue": "/fd \"SHA256\""
-                            },
-                            {
-                                "parameterName": "PageHash",
-                                "parameterValue": "/NPH"
-                            },
-                            {
-                                "parameterName": "TimeStamp",
-                                "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
-                            }
-                        ],
-                        "ToolName": "sign",
-                        "ToolVersion": "1.0"
-                    },
-                    {
-                        "KeyCode": "CP-230012",
-                        "OperationSetCode": "SigntoolVerify",
-                        "Parameters": [],
-                        "ToolName": "sign",
-                        "ToolVersion": "1.0"
-                    }
-                ]
-            }
-        }
-    ]
-}
diff --git a/build/config/ESRPSigning_Terminal.json b/build/config/ESRPSigning_Terminal.json
deleted file mode 100644
index 01780c2df3c..00000000000
--- a/build/config/ESRPSigning_Terminal.json
+++ /dev/null
@@ -1,72 +0,0 @@
-{
-    "Version": "1.0.0",
-    "UseMinimatch": false,
-    "SignBatches": [
-        {
-            "MatchedPath": [
-                // Namespaced DLLs
-                "Microsoft.Terminal.*.dll",
-                "Microsoft.Terminal.*.winmd",
-
-                // ConPTY and DefTerm
-                "OpenConsole.exe",
-                "OpenConsoleProxy.dll",
-
-                // VCRT Forwarders
-                "*_app.dll",
-
-                // Legacy DLLs with old names
-                "TerminalApp.dll",
-                "TerminalApp.winmd",
-                "TerminalConnection.dll",
-                "TerminalThemeHelpers.dll",
-                "WindowsTerminalShellExt.dll",
-
-                // The rest
-                "TerminalAzBridge.exe",
-                "wt.exe",
-                "WindowsTerminal.exe",
-                "elevate-shim.exe"
-            ],
-            "SigningInfo": {
-                "Operations": [
-                    {
-                        "KeyCode": "CP-230012",
-                        "OperationSetCode": "SigntoolSign",
-                        "Parameters": [
-                            {
-                                "parameterName": "OpusName",
-                                "parameterValue": "Microsoft"
-                            },
-                            {
-                                "parameterName": "OpusInfo",
-                                "parameterValue": "http://www.microsoft.com"
-                            },
-                            {
-                                "parameterName": "FileDigest",
-                                "parameterValue": "/fd \"SHA256\""
-                            },
-                            {
-                                "parameterName": "PageHash",
-                                "parameterValue": "/NPH"
-                            },
-                            {
-                                "parameterName": "TimeStamp",
-                                "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
-                            }
-                        ],
-                        "ToolName": "sign",
-                        "ToolVersion": "1.0"
-                    },
-                    {
-                        "KeyCode": "CP-230012",
-                        "OperationSetCode": "SigntoolVerify",
-                        "Parameters": [],
-                        "ToolName": "sign",
-                        "ToolVersion": "1.0"
-                    }
-                ]
-            }
-        }
-    ]
-}
diff --git a/build/config/esrp.build.batch.conpty.json b/build/config/esrp.build.batch.conpty.json
new file mode 100644
index 00000000000..b0097056ec5
--- /dev/null
+++ b/build/config/esrp.build.batch.conpty.json
@@ -0,0 +1,47 @@
+[
+    {
+        "MatchedPath": [
+            "conpty.dll",
+            "OpenConsole.exe"
+        ],
+        "SigningInfo": {
+            "Operations": [
+                {
+                    "KeyCode": "CP-230012",
+                    "OperationSetCode": "SigntoolSign",
+                    "Parameters": [
+                        {
+                            "parameterName": "OpusName",
+                            "parameterValue": "Microsoft"
+                        },
+                        {
+                            "parameterName": "OpusInfo",
+                            "parameterValue": "http://www.microsoft.com"
+                        },
+                        {
+                            "parameterName": "FileDigest",
+                            "parameterValue": "/fd \"SHA256\""
+                        },
+                        {
+                            "parameterName": "PageHash",
+                            "parameterValue": "/NPH"
+                        },
+                        {
+                            "parameterName": "TimeStamp",
+                            "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+                        }
+                    ],
+                    "ToolName": "sign",
+                    "ToolVersion": "1.0"
+                },
+                {
+                    "KeyCode": "CP-230012",
+                    "OperationSetCode": "SigntoolVerify",
+                    "Parameters": [],
+                    "ToolName": "sign",
+                    "ToolVersion": "1.0"
+                }
+            ]
+        }
+    }
+]
diff --git a/build/config/esrp.build.batch.terminal_constituents.json b/build/config/esrp.build.batch.terminal_constituents.json
new file mode 100644
index 00000000000..c33123a16de
--- /dev/null
+++ b/build/config/esrp.build.batch.terminal_constituents.json
@@ -0,0 +1,65 @@
+[
+    {
+        "MatchedPath": [
+            // Namespaced DLLs
+            "PackageContents/Microsoft.Terminal.*.dll",
+            "PackageContents/Microsoft.Terminal.*.winmd",
+
+            // ConPTY and DefTerm
+            "PackageContents/OpenConsole.exe",
+            "PackageContents/OpenConsoleProxy.dll",
+
+            // Legacy DLLs with old names
+            "PackageContents/TerminalApp.dll",
+            "PackageContents/TerminalApp.winmd",
+            "PackageContents/TerminalConnection.dll",
+            "PackageContents/TerminalThemeHelpers.dll",
+            "PackageContents/WindowsTerminalShellExt.dll",
+
+            // The rest
+            "PackageContents/TerminalAzBridge.exe",
+            "PackageContents/wt.exe",
+            "PackageContents/WindowsTerminal.exe",
+            "PackageContents/elevate-shim.exe"
+        ],
+        "SigningInfo": {
+            "Operations": [
+                {
+                    "KeyCode": "CP-230012",
+                    "OperationSetCode": "SigntoolSign",
+                    "Parameters": [
+                        {
+                            "parameterName": "OpusName",
+                            "parameterValue": "Microsoft"
+                        },
+                        {
+                            "parameterName": "OpusInfo",
+                            "parameterValue": "http://www.microsoft.com"
+                        },
+                        {
+                            "parameterName": "FileDigest",
+                            "parameterValue": "/fd \"SHA256\""
+                        },
+                        {
+                            "parameterName": "PageHash",
+                            "parameterValue": "/NPH"
+                        },
+                        {
+                            "parameterName": "TimeStamp",
+                            "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+                        }
+                    ],
+                    "ToolName": "sign",
+                    "ToolVersion": "1.0"
+                },
+                {
+                    "KeyCode": "CP-230012",
+                    "OperationSetCode": "SigntoolVerify",
+                    "Parameters": [],
+                    "ToolName": "sign",
+                    "ToolVersion": "1.0"
+                }
+            ]
+        }
+    }
+]
diff --git a/build/config/esrp.build.batch.wpf.json b/build/config/esrp.build.batch.wpf.json
new file mode 100644
index 00000000000..e9c9af87891
--- /dev/null
+++ b/build/config/esrp.build.batch.wpf.json
@@ -0,0 +1,46 @@
+[
+    {
+        "MatchedPath": [
+            "PublicTerminalCore.dll"
+        ],
+        "SigningInfo": {
+            "Operations": [
+                {
+                    "KeyCode": "CP-230012",
+                    "OperationSetCode": "SigntoolSign",
+                    "Parameters": [
+                        {
+                            "parameterName": "OpusName",
+                            "parameterValue": "Microsoft"
+                        },
+                        {
+                            "parameterName": "OpusInfo",
+                            "parameterValue": "http://www.microsoft.com"
+                        },
+                        {
+                            "parameterName": "FileDigest",
+                            "parameterValue": "/fd \"SHA256\""
+                        },
+                        {
+                            "parameterName": "PageHash",
+                            "parameterValue": "/NPH"
+                        },
+                        {
+                            "parameterName": "TimeStamp",
+                            "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+                        }
+                    ],
+                    "ToolName": "sign",
+                    "ToolVersion": "1.0"
+                },
+                {
+                    "KeyCode": "CP-230012",
+                    "OperationSetCode": "SigntoolVerify",
+                    "Parameters": [],
+                    "ToolName": "sign",
+                    "ToolVersion": "1.0"
+                }
+            ]
+        }
+    }
+]
diff --git a/build/config/esrp.build.batch.wpfdotnet.json b/build/config/esrp.build.batch.wpfdotnet.json
new file mode 100644
index 00000000000..0699353f64c
--- /dev/null
+++ b/build/config/esrp.build.batch.wpfdotnet.json
@@ -0,0 +1,47 @@
+[
+    {
+        "MatchedPath": [
+            "WpfTerminalControl/net472/Microsoft.Terminal.Wpf.dll",
+            "WpfTerminalControl/net6.0-windows/Microsoft.Terminal.Wpf.dll"
+        ],
+        "SigningInfo": {
+            "Operations": [
+                {
+                    "KeyCode": "CP-230012",
+                    "OperationSetCode": "SigntoolSign",
+                    "Parameters": [
+                        {
+                            "parameterName": "OpusName",
+                            "parameterValue": "Microsoft"
+                        },
+                        {
+                            "parameterName": "OpusInfo",
+                            "parameterValue": "http://www.microsoft.com"
+                        },
+                        {
+                            "parameterName": "FileDigest",
+                            "parameterValue": "/fd \"SHA256\""
+                        },
+                        {
+                            "parameterName": "PageHash",
+                            "parameterValue": "/NPH"
+                        },
+                        {
+                            "parameterName": "TimeStamp",
+                            "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+                        }
+                    ],
+                    "ToolName": "sign",
+                    "ToolVersion": "1.0"
+                },
+                {
+                    "KeyCode": "CP-230012",
+                    "OperationSetCode": "SigntoolVerify",
+                    "Parameters": [],
+                    "ToolName": "sign",
+                    "ToolVersion": "1.0"
+                }
+            ]
+        }
+    }
+]
diff --git a/build/pgo/PGO.DB.proj b/build/pgo/PGO.DB.proj
index f89eff931ac..ce7e1c6ebcb 100644
--- a/build/pgo/PGO.DB.proj
+++ b/build/pgo/PGO.DB.proj
@@ -1,4 +1,5 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="Terminal.PGO.props" />
+  <Import Project="$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.props" />
   <Import Project="$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets" />
-</Project>
\ No newline at end of file
+</Project>
diff --git a/build/pgo/Terminal.PGO.DB.nuspec b/build/pgo/Terminal.PGO.DB.nuspec
index d49574423a8..b1437af2a0b 100644
--- a/build/pgo/Terminal.PGO.DB.nuspec
+++ b/build/pgo/Terminal.PGO.DB.nuspec
@@ -12,5 +12,6 @@
   <files>
     <!-- The target directories for pgd files need to remain as is.  PGO optimization pass will rely on this exact directory layout. -->
     <file src="x64\*.pgd" target="tools\x64"/>
+    <file src="arm64\*.pgd" target="tools\arm64"/>
   </files>
 </package>
diff --git a/build/pgo/Terminal.PGO.props b/build/pgo/Terminal.PGO.props
index dedf48a6723..109bdd7ffa0 100644
--- a/build/pgo/Terminal.PGO.props
+++ b/build/pgo/Terminal.PGO.props
@@ -46,6 +46,5 @@
     <PGOCopyRuntime Condition="'$(PGOBuildMode)' == 'Instrument'">true</PGOCopyRuntime>
   </PropertyGroup>
 
-  <!-- Import PGO-Helpers -->
-  <Import Project="$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.props" />
+  <!-- Do not import PGO-Helpers props here, as it is too early to detect the C++ tools directory -->
 </Project>
diff --git a/build/pipelines/ci.yml b/build/pipelines/ci.yml
index 3b833d8e718..3a142688443 100644
--- a/build/pipelines/ci.yml
+++ b/build/pipelines/ci.yml
@@ -35,11 +35,7 @@ parameters:
     type: boolean
     default: true
   - name: runTests
-    displayName: "Run Unit and Feature Tests"
-    type: boolean
-    default: true
-  - name: submitHelix
-    displayName: "Submit to Helix for testing and PGO"
+    displayName: "Run Tests"
     type: boolean
     default: true
   - name: buildPlatforms
@@ -54,28 +50,43 @@ stages:
     - stage: Audit_x64
       displayName: Audit Mode
       dependsOn: []
-      condition: succeeded()
       jobs:
-        - template: ./templates/build-console-audit-job.yml
+        - template: ./templates-v2/job-build-project.yml
           parameters:
-            platform: x64
+            pool:
+              ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+                name: SHINE-OSS-L
+              ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+                name: SHINE-INT-L
+            buildPlatforms: [x64]
+            buildConfigurations: [AuditMode]
+            buildEverything: true
+            keepAllExpensiveBuildOutputs: false
 
-  - stage: Scripts
-    displayName: Code Health Scripts
+  - stage: CodeHealth
+    displayName: Code Health
     dependsOn: []
-    condition: succeeded()
     jobs:
-      - template: ./templates/check-formatting.yml
+      - template: ./templates-v2/job-check-code-format.yml
 
   - ${{ each platform in parameters.buildPlatforms }}:
     - stage: Build_${{ platform }}
       displayName: Build ${{ platform }}
       dependsOn: []
-      condition: succeeded()
       jobs:
-        - template: ./templates/build-console-ci.yml
+        - template: ./templates-v2/job-build-project.yml
           parameters:
-            platform: ${{ platform }}
+            pool:
+              ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+                name: SHINE-OSS-L
+              ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+                name: SHINE-INT-L
+            buildPlatforms:
+              - ${{ platform }}
+            buildConfigurations: [Release]
+            buildEverything: true
+            keepAllExpensiveBuildOutputs: false
+
     - ${{ if eq(parameters.runTests, true) }}:
       - stage: Test_${{ platform }}
         displayName: Test ${{ platform }}
@@ -83,22 +94,13 @@ stages:
           - Build_${{ platform }}
         condition: succeeded()
         jobs:
-          - template: ./templates/test-console-ci.yml
+          - template: ./templates-v2/job-test-project.yml
             parameters:
               platform: ${{ platform }}
 
-  - ${{ if and(containsValue(parameters.buildPlatforms, 'x64'), eq(parameters.submitHelix, true), ne(variables['Build.Reason'], 'PullRequest')) }}:
-    - stage: Helix_x64
-      displayName: Helix x64
-      dependsOn: [Build_x64]
-      jobs:
-        - template: ./templates/console-ci-helix-job.yml
-          parameters:
-            platform: x64
-
-  - ${{ if and(containsValue(parameters.buildPlatforms, 'x64'), ne(variables['Build.Reason'], 'PullRequest')) }}:
+  - ${{ if ne(variables['Build.Reason'], 'PullRequest') }}:
     - stage: CodeIndexer
       displayName: Github CodeNav Indexer
-      dependsOn: [Build_x64]
+      dependsOn: []
       jobs:
-        - template: ./templates/codenav-indexer.yml
+        - template: ./templates-v2/job-index-github-codenav.yml
diff --git a/build/pipelines/daily-loc-submission.yml b/build/pipelines/daily-loc-submission.yml
index 126b2e10359..890c1f86a47 100644
--- a/build/pipelines/daily-loc-submission.yml
+++ b/build/pipelines/daily-loc-submission.yml
@@ -27,6 +27,7 @@ steps:
   clean: true
   submodules: false
   fetchDepth: 1 # Don't need a deep checkout for loc files!
+  fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
   persistCredentials: true
   path: s # Adding a second repo made Azure DevOps change where we're checked out.
 
diff --git a/build/pipelines/feature-flag-ci.yml b/build/pipelines/feature-flag-ci.yml
index 0ca4400463d..ead379dbf36 100644
--- a/build/pipelines/feature-flag-ci.yml
+++ b/build/pipelines/feature-flag-ci.yml
@@ -7,6 +7,7 @@ pr:
   paths:
     include:
       - src/features.xml
+      - build/pipelines/feature-flag-ci.yml
 
 variables:
   - name: runCodesignValidationInjectionBG
@@ -21,9 +22,19 @@ parameters:
       # Dev is built automatically
       # WindowsInbox does not typically build with VS.
 
-jobs:
+stages:
   - ${{ each branding in parameters.buildBrandings }}:
-    - template: ./templates/build-console-ci.yml
-      parameters:
-        platform: x64
-        branding: ${{ branding }}
+    - stage: Build_${{ branding }}
+      dependsOn: []
+      displayName: Build ${{ branding }}
+      jobs:
+        - template: ./templates-v2/job-build-project.yml
+          parameters:
+            pool: # This only runs in CI
+                name: SHINE-OSS-L
+            buildPlatforms: [x64]
+            buildConfigurations: [Release]
+            buildEverything: true
+            branding: ${{ branding }}
+            keepAllExpensiveBuildOutputs: false
+            artifactStem: -${{ branding }} # Disambiguate artifacts with the same config/platform
diff --git a/build/pipelines/fuzz.yml b/build/pipelines/fuzz.yml
index b5604b5f227..75b781d33c6 100644
--- a/build/pipelines/fuzz.yml
+++ b/build/pipelines/fuzz.yml
@@ -16,44 +16,52 @@ pr: none
 name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
 
 stages:
-  - stage: Build_Fuzz_Config
-    displayName: Build Fuzzers
+  - stage: Build
+    displayName: Fuzzing Build
     dependsOn: []
     condition: succeeded()
     jobs:
-    - template: ./templates/build-console-fuzzing.yml
-      parameters:
-        platform: x64
-  - stage: OneFuzz
-    displayName: Submit OneFuzz Job
-    dependsOn: ['Build_Fuzz_Config']
+      - template: ./templates-v2/job-build-project.yml
+        parameters:
+          pool:
+            ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+              name: SHINE-OSS-L
+            ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+              name: SHINE-INT-L
+          buildPlatforms: [x64]
+          buildConfigurations: [Fuzzing]
+          buildEverything: true
+          keepAllExpensiveBuildOutputs: false
+
+  - stage: Submit
+    displayName: Submit to OneFuzz
+    dependsOn: [Build]
     condition: succeeded()
-    pool:
-      vmImage: 'ubuntu-latest'
-    variables:
-      artifactName: fuzzingBuildOutput
     jobs:
-    - job:
-      steps:
-      - task: DownloadBuildArtifacts@0
-        inputs:
-          artifactName: $(artifactName)
-          downloadPath: $(Build.ArtifactStagingDirectory)
-      - task: UsePythonVersion@0
-        inputs:
-          versionSpec: '3.x'
-          addToPath: true
-          architecture: 'x64'
-      - bash: |
-          set -ex
-          pip -q install onefuzz
-          onefuzz config --endpoint $(endpoint) --client_id $(client_id) --authority $(authority) --tenant_domain $(tenant_domain) --client_secret $(client_secret)
-          sed -i s/INSERT_PAT_HERE/$(ado_pat)/ build/Fuzz/notifications-ado.json
-          sed -i s/INSERT_ASSIGNED_HERE/$(ado_assigned_to)/ build/Fuzz/notifications-ado.json
-        displayName: Configure OneFuzz
-      - bash: |
-          onefuzz template libfuzzer basic --colocate_all_tasks --vm_count 1 --target_exe $target_exe_path --notification_config @./build/Fuzz/notifications-ado.json OpenConsole $test_name $(Build.SourceVersion) default
-        displayName: Submit OneFuzz Job
-        env:
-          target_exe_path: $(Build.ArtifactStagingDirectory)/$(artifactName)/Fuzzing/x64/test/OpenConsoleFuzzer.exe
-          test_name: WriteCharsLegacy
+      - job:
+        pool:
+          vmImage: 'ubuntu-latest'
+        steps:
+        - task: DownloadPipelineArtifact@2
+          displayName: Download artifacts
+          inputs:
+            artifactName: build-x64-Fuzzing
+            downloadPath: $(Build.ArtifactStagingDirectory)
+        - task: UsePythonVersion@0
+          inputs:
+            versionSpec: '3.x'
+            addToPath: true
+            architecture: 'x64'
+        - bash: |
+            set -ex
+            pip -q install onefuzz
+            onefuzz config --endpoint $(endpoint) --client_id $(client_id) --authority $(authority) --tenant_domain $(tenant_domain) --client_secret $(client_secret)
+            sed -i s/INSERT_PAT_HERE/$(ado_pat)/ build/Fuzz/notifications-ado.json
+            sed -i s/INSERT_ASSIGNED_HERE/$(ado_assigned_to)/ build/Fuzz/notifications-ado.json
+          displayName: Configure OneFuzz
+        - bash: |
+            onefuzz template libfuzzer basic --colocate_all_tasks --vm_count 1 --target_exe $target_exe_path --notification_config @./build/Fuzz/notifications-ado.json OpenConsole $test_name $(Build.SourceVersion) default
+          displayName: Submit OneFuzz Job
+          env:
+            target_exe_path: $(Build.ArtifactStagingDirectory)/OpenConsoleFuzzer.exe
+            test_name: WriteCharsLegacy
diff --git a/build/pipelines/pgo.yml b/build/pipelines/pgo.yml
index 1c93a56d8cd..d659c4e2520 100644
--- a/build/pipelines/pgo.yml
+++ b/build/pipelines/pgo.yml
@@ -1,5 +1,27 @@
 trigger: none
 pr: none
+schedules:
+  - cron: "0 5 * * 2-6" # Run at 05:00 UTC Tuesday through Saturday (Even later than Localization, after the work day in Pacific, Mon-Fri)
+    displayName: "Nightly Instrumentation Build"
+    branches:
+      include:
+        - main
+    always: false # only run if there's code changes!
+
+parameters:
+  - name: branding
+    displayName: "Branding (Build Type)"
+    type: string
+    default: Preview # By default, we'll PGO the Preview builds to get max coverage
+    values:
+      - Release
+      - Preview
+      - Dev
+  - name: buildPlatforms
+    type: object
+    default:
+      - x64
+      - arm64
 
 variables:
   - name: runCodesignValidationInjectionBG
@@ -10,18 +32,57 @@ variables:
 name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
 
 stages:
-  - stage: Build_x64
-    displayName: Build x64
+  - stage: Build
+    displayName: Build
     dependsOn: []
     condition: succeeded()
     jobs:
-      - template: ./templates/build-console-pgo.yml
+      - template: ./templates-v2/job-build-project.yml
         parameters:
-          platform: x64
-  - stage: Publish_PGO_Databases
-    displayName: Publish PGO databases
-    dependsOn: ['Build_x64']
+          pool:
+            ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+              name: SHINE-OSS-L
+            ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+              name: SHINE-INT-L
+          branding: ${{ parameters.branding }}
+          buildPlatforms: ${{ parameters.buildPlatforms }}
+          buildConfigurations: [Release]
+          buildEverything: true
+          pgoBuildMode: Instrument
+          artifactStem: -instrumentation
+
+  - stage: RunPGO
+    displayName: Run PGO
+    dependsOn: [Build]
+    condition: succeeded()
+    jobs:
+      - ${{ each platform in parameters.buildPlatforms }}:
+        - template: ./templates-v2/job-run-pgo-tests.yml
+          parameters:
+            # This job chooses its own pools based on platform
+            buildPlatform: ${{ platform }}
+            buildConfiguration: Release
+            artifactStem: -instrumentation
+
+  - stage: FinalizePGO
+    displayName: Finalize PGO and Publish
+    dependsOn: [RunPGO]
+    condition: succeeded()
     jobs:
-      - template: ./templates/pgo-build-and-publish-nuget-job.yml
+      # This job takes multiple platforms and fans them back in to a single artifact.
+      - template: ./templates-v2/job-pgo-merge-pgd.yml
+        parameters:
+          jobName: MergePGD
+          pool:
+            vmImage: 'windows-2022'
+          buildConfiguration: Release
+          buildPlatforms: ${{ parameters.buildPlatforms }}
+          artifactStem: -instrumentation
+
+      - template: ./templates-v2/job-pgo-build-nuget-and-publish.yml
         parameters:
-          pgoArtifact: 'PGO'
+          pool:
+            vmImage: 'windows-2022'
+          dependsOn: MergePGD
+          buildConfiguration: Release
+          artifactStem: -instrumentation
diff --git a/build/pipelines/release.yml b/build/pipelines/release.yml
index cea5c7d0fc5..fd388ea1d0e 100644
--- a/build/pipelines/release.yml
+++ b/build/pipelines/release.yml
@@ -14,22 +14,11 @@ parameters:
     values:
       - Release
       - Preview
+      - Dev
   - name: buildTerminal
     displayName: "Build Windows Terminal MSIX"
     type: boolean
     default: true
-  - name: runCompliance
-    displayName: "Run Compliance and Security Build"
-    type: boolean
-    default: true
-  - name: publishSymbolsToPublic
-    displayName: "Publish Symbols to MSDL"
-    type: boolean
-    default: true
-  - name: buildTerminalVPack
-    displayName: "Build Windows Terminal VPack"
-    type: boolean
-    default: false
   - name: buildConPTY
     displayName: "Build ConPTY NuGet"
     type: boolean
@@ -47,19 +36,44 @@ parameters:
       - Instrument
       - None
   - name: buildConfigurations
+    displayName: "Build Configurations"
     type: object
     default:
       - Release
   - name: buildPlatforms
+    displayName: "Build Platforms"
     type: object
     default:
       - x64
       - x86
       - arm64
+  - name: codeSign
+    displayName: "Sign all build outputs"
+    type: boolean
+    default: true
+  - name: generateSbom
+    displayName: "Generate a Bill of Materials"
+    type: boolean
+    default: true
+  - name: terminalInternalPackageVersion
+    displayName: "Terminal Internal Package Version"
+    type: string
+    default: '0.0.8'
+
+  - name: runCompliance
+    displayName: "Run Compliance and Security Build"
+    type: boolean
+    default: true
+  - name: publishSymbolsToPublic
+    displayName: "Publish Symbols to MSDL"
+    type: boolean
+    default: true
+  - name: publishVpackToWindows
+    displayName: "Publish VPack to Windows"
+    type: boolean
+    default: false
 
 variables:
-  MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe'
-  TerminalInternalPackageVersion: "0.0.8"
   # If we are building a branch called "release-*", change the NuGet suffix
   # to "preview". If we don't do that, XES will set the suffix to "release1"
   # because it truncates the value after the first period.
@@ -84,671 +98,113 @@ variables:
     NuGetPackBetaVersion: experimental
 
 name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
+
 resources:
   repositories:
   - repository: self
     type: git
     ref: main
-jobs:
-- job: Build
-  pool:
-    name: SHINE-INT-L # Run the compilation on the large agent pool, rather than the default small one.
-    demands: ImageOverride -equals SHINE-VS17-Latest
-  strategy:
-    matrix:
-      ${{ each config in parameters.buildConfigurations }}:
-        ${{ each platform in parameters.buildPlatforms }}:
-          ${{ config }}_${{ platform }}:
-            BuildConfiguration: ${{ config }}
-            BuildPlatform: ${{ platform }}
-  displayName: Build
-  timeoutInMinutes: 240
-  cancelTimeoutInMinutes: 1
-  steps:
-  - checkout: self
-    clean: true
-    submodules: true
-    persistCredentials: True
-  - task: PkgESSetupBuild@12
-    displayName: Package ES - Setup Build
-    inputs:
-      disableOutputRedirect: true
-  - task: PowerShell@2
-    displayName: Rationalize Build Platform
-    inputs:
-      targetType: inline
-      script: >-
-        $Arch = "$(BuildPlatform)"
-
-        If ($Arch -Eq "x86") { $Arch = "Win32" }
-
-        Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
-  - template: .\templates\restore-nuget-steps.yml
-  - task: UniversalPackages@0
-    displayName: Download terminal-internal Universal Package
-    inputs:
-      feedListDownload: 2b3f8893-a6e8-411f-b197-a9e05576da48
-      packageListDownload: e82d490c-af86-4733-9dc4-07b772033204
-      versionListDownload: $(TerminalInternalPackageVersion)
-  - task: TouchdownBuildTask@1
-    displayName: Download Localization Files
-    inputs:
-      teamId: 7105
-      authId: $(TouchdownAppId)
-      authKey: $(TouchdownAppKey)
-      resourceFilePath: >-
-        src\cascadia\TerminalApp\Resources\en-US\Resources.resw
-
-        src\cascadia\TerminalApp\Resources\en-US\ContextMenu.resw
-
-        src\cascadia\TerminalControl\Resources\en-US\Resources.resw
-
-        src\cascadia\TerminalConnection\Resources\en-US\Resources.resw
-
-        src\cascadia\TerminalSettingsModel\Resources\en-US\Resources.resw
-
-        src\cascadia\TerminalSettingsEditor\Resources\en-US\Resources.resw
-
-        src\cascadia\CascadiaPackage\Resources\en-US\Resources.resw
-      appendRelativeDir: true
-      localizationTarget: false
-      pseudoSetting: Included
-  - task: PowerShell@2
-    displayName: Move Loc files one level up
-    inputs:
-      targetType: inline
-      script: >-
-        $Files = Get-ChildItem . -R -Filter 'Resources.resw' | ? FullName -Like '*en-US\*\Resources.resw'
-
-        $Files | % { Move-Item -Verbose $_.Directory $_.Directory.Parent.Parent -EA:Ignore }
-      pwsh: true
-  - task: PowerShell@2
-    displayName: Copy the Context Menu Loc Resources to CascadiaPackage
-    inputs:
-      filePath: ./build/scripts/Copy-ContextMenuResourcesToCascadiaPackage.ps1
-      pwsh: true
-  - task: PowerShell@2
-    displayName: Generate NOTICE.html from NOTICE.md
-    inputs:
-      filePath: .\build\scripts\Generate-ThirdPartyNotices.ps1
-      arguments: -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
-      pwsh: true
-  - ${{ if eq(parameters.buildTerminal, true) }}:
-    - task: VSBuild@1
-      displayName: Build solution **\OpenConsole.sln
-      condition: true
-      inputs:
-        solution: '**\OpenConsole.sln'
-        msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /t:Terminal\CascadiaPackage /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
-        platform: $(BuildPlatform)
-        configuration: $(BuildConfiguration)
-        clean: true
-        maximumCpuCount: true
-    - task: PublishBuildArtifacts@1
-      displayName: 'Publish Artifact: binlog'
-      condition: failed()
-      continueOnError: True
-      inputs:
-        PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog
-        ArtifactName: binlog-$(BuildPlatform)
-    - task: PowerShell@2
-      displayName: Check MSIX for common regressions
-      inputs:
-        targetType: inline
-        script: >-
-          $Package = Get-ChildItem -Recurse -Filter "CascadiaPackage_*.msix"
-
-          .\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path $Package.FullName
-        pwsh: true
-  - ${{ if eq(parameters.buildWPF, true) }}:
-    - task: VSBuild@1
-      displayName: Build solution **\OpenConsole.sln for PublicTerminalCore
-      inputs:
-        solution: '**\OpenConsole.sln'
-        msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
-        platform: $(BuildPlatform)
-        configuration: $(BuildConfiguration)
-  - ${{ if eq(parameters.buildConPTY, true) }}:
-    - task: VSBuild@1
-      displayName: Build solution **\OpenConsole.sln for ConPTY
-      inputs:
-        solution: '**\OpenConsole.sln'
-        msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Conhost\Host_EXE;Conhost\winconpty_DLL
-        platform: $(BuildPlatform)
-        configuration: $(BuildConfiguration)
-  - task: PowerShell@2
-    displayName: Source Index PDBs
-    inputs:
-      filePath: build\scripts\Index-Pdbs.ps1
-      arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
-      errorActionPreference: silentlyContinue
-      pwsh: true
-  - task: PowerShell@2
-    displayName: Run Unit Tests
-    condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
-    enabled: False
-    inputs:
-      filePath: build\scripts\Run-Tests.ps1
-      arguments: -MatchPattern '*unit.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
-  - task: PowerShell@2
-    displayName: Run Feature Tests
-    condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
-    enabled: False
-    inputs:
-      filePath: build\scripts\Run-Tests.ps1
-      arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
-  - ${{ if eq(parameters.buildTerminal, true) }}:
-    - task: CopyFiles@2
-      displayName: Copy *.msix and symbols to Artifacts
-      inputs:
-        Contents: >-
-          **/*.msix
-
-          **/*.appxsym
-        TargetFolder: $(Build.ArtifactStagingDirectory)/appx
-        OverWrite: true
-        flattenFolders: true
-
-    - pwsh: |-
-        $Package = (Get-ChildItem "$(Build.ArtifactStagingDirectory)/appx" -Recurse -Filter "Cascadia*.msix" | Select -First 1)
-        $PackageFilename = $Package.FullName
-        Write-Host "##vso[task.setvariable variable=WindowsTerminalPackagePath]${PackageFilename}"
-        & "$(MakeAppxPath)" unpack /p $PackageFilename /d "$(Build.SourcesDirectory)\UnpackedTerminalPackage"
-      displayName: Unpack the new Terminal package for signing
-
-    - task: EsrpCodeSigning@1
-      displayName: Submit Terminal's binaries for signing
-      inputs:
-        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
-        FolderPath: '$(Build.SourcesDirectory)\UnpackedTerminalPackage'
-        signType: batchSigning
-        batchSignPolicyFile: '$(Build.SourcesDirectory)\build\config\ESRPSigning_Terminal.json'
-
-    - pwsh: |-
-        $PackageFilename = "$(WindowsTerminalPackagePath)"
-        Remove-Item "$(Build.SourcesDirectory)\UnpackedTerminalPackage\CodeSignSummary*"
-        & "$(MakeAppxPath)" pack /h SHA256 /o /p $PackageFilename /d "$(Build.SourcesDirectory)\UnpackedTerminalPackage"
-      displayName: Re-pack the new Terminal package after signing
-
-    - pwsh: |-
-        $XamlAppxPath = (Get-Item "src\cascadia\CascadiaPackage\AppPackages\*\Dependencies\$(BuildPlatform)\Microsoft.UI.Xaml*.appx").FullName
-        & .\build\scripts\New-UnpackagedTerminalDistribution.ps1 -TerminalAppX $(WindowsTerminalPackagePath) -XamlAppX $XamlAppxPath -Destination "$(Build.ArtifactStagingDirectory)/appx"
-      displayName: Build Unpackaged Distribution
-
-    - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
-      displayName: 'Generate SBOM manifest (application)'
-      inputs:
-       BuildDropPath: '$(System.ArtifactsDirectory)/appx'
-
-    - task: DropValidatorTask@0
-      displayName: 'Validate application SBOM manifest'
-      inputs:
-        BuildDropPath: '$(System.ArtifactsDirectory)/appx'
-        OutputPath: 'output.json'
-        ValidateSignature: true
-        Verbosity: 'Verbose'
-
-    - task: PublishBuildArtifacts@1
-      displayName: Publish Artifact (Terminal app)
-      inputs:
-        PathtoPublish: $(Build.ArtifactStagingDirectory)/appx
-        ArtifactName: terminal-$(BuildPlatform)-$(BuildConfiguration)
-
-  - ${{ if eq(parameters.buildConPTY, true) }}:
-    - task: CopyFiles@2
-      displayName: Copy ConPTY to Artifacts
-      inputs:
-        Contents: |-
-          $(Build.SourcesDirectory)/bin/**/conpty.dll
-          $(Build.SourcesDirectory)/bin/**/conpty.lib
-          $(Build.SourcesDirectory)/bin/**/conpty.pdb
-          $(Build.SourcesDirectory)/bin/**/OpenConsole.exe
-          $(Build.SourcesDirectory)/bin/**/OpenConsole.pdb
-        TargetFolder: $(Build.ArtifactStagingDirectory)/conpty
-        OverWrite: true
-        flattenFolders: true
-    - task: PublishBuildArtifacts@1
-      displayName: Publish Artifact (ConPTY)
-      inputs:
-        PathtoPublish: $(Build.ArtifactStagingDirectory)/conpty
-        ArtifactName: conpty-dll-$(BuildPlatform)-$(BuildConfiguration)
-  - ${{ if eq(parameters.buildWPF, true) }}:
-    - task: CopyFiles@2
-      displayName: Copy PublicTerminalCore.dll to Artifacts
-      inputs:
-        Contents: >-
-          **/PublicTerminalCore.dll
-        TargetFolder: $(Build.ArtifactStagingDirectory)/wpf
-        OverWrite: true
-        flattenFolders: true
-    - task: PublishBuildArtifacts@1
-      displayName: Publish Artifact (PublicTerminalCore)
-      inputs:
-        PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf
-        ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)
-
-  - task: PublishSymbols@2
-    displayName: Publish symbols path
-    continueOnError: True
-    inputs:
-      SearchPattern: |
-        $(Build.SourcesDirectory)/bin/**/*.pdb
-        $(Build.SourcesDirectory)/bin/**/*.exe
-        $(Build.SourcesDirectory)/bin/**/*.dll
-      IndexSources: false
-      SymbolServerType: TeamServices
-
-- ${{ if eq(parameters.runCompliance, true) }}:
-  - template: ./templates/build-console-compliance-job.yml
-
-- ${{ if eq(parameters.buildTerminal, true) }}:
-  - job: BundleAndSign
-    displayName: Create and sign AppX/MSIX bundles
-    variables:
-      ${{ if eq(parameters.branding, 'Release') }}:
-        BundleStemName: Microsoft.WindowsTerminal
-      ${{ elseif eq(parameters.branding, 'Preview') }}:
-        BundleStemName: Microsoft.WindowsTerminalPreview
-      ${{ else }}:
-        BundleStemName: WindowsTerminalDev
-    dependsOn: Build
-    steps:
-    - checkout: self
-      clean: true
-      fetchDepth: 1
-      submodules: true
-      persistCredentials: True
-    - task: PkgESSetupBuild@12
-      displayName: Package ES - Setup Build
-      inputs:
-        disableOutputRedirect: true
-    - ${{ each platform in parameters.buildPlatforms }}:
-      - task: DownloadBuildArtifacts@1
-        displayName: Download Artifacts ${{ platform }}
-        inputs:
-          # Make sure to download the entire artifact, because it includes the SPDX SBOM
-          artifactName: terminal-${{ platform }}-Release
-          # Downloading to the source directory should ensure that the later SBOM generator can see the earlier SBOMs.
-          downloadPath: '$(Build.SourcesDirectory)/appx-artifacts'
-    # Add 3000 to the major version component, but only for the bundle.
-    # This is to ensure that it is newer than "2022.xx.yy.zz" or whatever the original bundle versions were before
-    # we switched to uniform naming.
-    - pwsh: |-
-        $VersionEpoch = 3000
-        $Components = "$(XES_APPXMANIFESTVERSION)" -Split "\."
-        $Components[0] = ([int]$Components[0] + $VersionEpoch)
-        $BundleVersion = $Components -Join "."
-        New-Item -Type Directory "$(System.ArtifactsDirectory)\bundle"
-        .\build\scripts\Create-AppxBundle.ps1 -InputPath "$(Build.SourcesDirectory)/appx-artifacts" -ProjectName CascadiaPackage -BundleVersion $BundleVersion -OutputPath "$(System.ArtifactsDirectory)\bundle\$(BundleStemName)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
-      displayName: Create WindowsTerminal*.msixbundle
-    - task: EsrpCodeSigning@1
-      displayName: Submit *.msixbundle to ESRP for code signing
-      inputs:
-        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
-        FolderPath: $(System.ArtifactsDirectory)\bundle
-        Pattern: $(BundleStemName)*.msixbundle
-        UseMinimatch: true
-        signConfigType: inlineSignParams
-        inlineOperation: >-
-          [
-              {
-                  "KeyCode": "Dynamic",
-                  "CertTemplateName": "WINMSAPP1ST",
-                  "CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
-                  "OperationCode": "SigntoolSign",
-                  "Parameters": {
-                      "OpusName": "Microsoft",
-                      "OpusInfo": "http://www.microsoft.com",
-                      "FileDigest": "/fd \"SHA256\"",
-                      "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
-                  },
-                  "ToolName": "sign",
-                  "ToolVersion": "1.0"
-              },
-              {
-                  "KeyCode": "Dynamic",
-                  "CertTemplateName": "WINMSAPP1ST",
-                  "CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
-                  "OperationCode": "SigntoolVerify",
-                  "Parameters": {},
-                  "ToolName": "sign",
-                  "ToolVersion": "1.0"
-              }
-          ]
-
-    - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
-      displayName: 'Generate SBOM manifest (bundle)'
-      inputs:
-       BuildDropPath: '$(System.ArtifactsDirectory)/bundle'
-       BuildComponentPath: '$(Build.SourcesDirectory)/appx-artifacts'
-
-    - task: DropValidatorTask@0
-      displayName: 'Validate bundle SBOM manifest'
-      inputs:
-        BuildDropPath: '$(System.ArtifactsDirectory)/bundle'
-        OutputPath: 'output.json'
-        ValidateSignature: true
-        Verbosity: 'Verbose'
-
-    - task: PublishBuildArtifacts@1
-      displayName: 'Publish Artifact: appxbundle-signed'
-      inputs:
-        PathtoPublish: $(System.ArtifactsDirectory)\bundle
-        ArtifactName: appxbundle-signed
-
-- ${{ if eq(parameters.buildConPTY, true) }}:
-  - job: PackageAndSignConPTY
-    strategy:
-      matrix:
-        ${{ each config in parameters.buildConfigurations }}:
-          ${{ config }}:
-            BuildConfiguration: ${{ config }}
-    displayName: Create NuGet Package (ConPTY)
-    dependsOn: Build
-    steps:
-    - checkout: self
-      clean: true
-      fetchDepth: 1
-      submodules: true
-      persistCredentials: True
-    - task: PkgESSetupBuild@12
-      displayName: Package ES - Setup Build
-      inputs:
-        disableOutputRedirect: true
-    - ${{ each platform in parameters.buildPlatforms }}:
-      - task: DownloadBuildArtifacts@1
-        displayName: Download ${{ platform }} ConPTY binaries
-        inputs:
-          artifactName: conpty-dll-${{ platform }}-$(BuildConfiguration)
-          downloadPath: bin\${{ platform }}\$(BuildConfiguration)\
-          extractTars: false
-    - task: PowerShell@2
-      displayName: Move downloaded artifacts around
-      inputs:
-        targetType: inline
-        # Find all artifact files and move them up a directory. Ugh.
-        script: |-
-          Get-ChildItem bin -Recurse -Directory -Filter conpty-dll-* | % {
-            $_ | Get-ChildItem -Recurse -File | % {
-              Move-Item -Verbose $_.FullName $_.Directory.Parent.FullName
-            }
-          }
-          Move-Item bin\x86 bin\Win32
-
-    - task: EsrpCodeSigning@1
-      displayName: Submit ConPTY libraries and OpenConsole for code signing
-      inputs:
-        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
-        FolderPath: '$(Build.SourcesDirectory)/bin'
-        signType: batchSigning
-        batchSignPolicyFile: '$(Build.SourcesDirectory)\build\config\ESRPSigning_ConPTY.json'
-
-    - task: NuGetToolInstaller@1
-      displayName: Use NuGet 5.10.0
-      inputs:
-        versionSpec: 5.10.0
-    - task: NuGetCommand@2
-      displayName: NuGet pack
-      inputs:
-        command: pack
-        packagesToPack: $(Build.SourcesDirectory)\src\winconpty\package\winconpty.nuspec
-        packDestination: '$(Build.ArtifactStagingDirectory)/nupkg'
-        versioningScheme: byEnvVar
-        versionEnvVar: XES_PACKAGEVERSIONNUMBER
-    - task: EsrpCodeSigning@1
-      displayName: Submit *.nupkg to ESRP for code signing
-      inputs:
-        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
-        FolderPath: $(Build.ArtifactStagingDirectory)/nupkg
-        Pattern: '*.nupkg'
-        UseMinimatch: true
-        signConfigType: inlineSignParams
-        inlineOperation: >-
-          [
-              {
-                  "KeyCode": "CP-401405",
-                  "OperationCode": "NuGetSign",
-                  "Parameters": {},
-                  "ToolName": "sign",
-                  "ToolVersion": "1.0"
-              },
-              {
-                  "KeyCode": "CP-401405",
-                  "OperationCode": "NuGetVerify",
-                  "Parameters": {},
-                  "ToolName": "sign",
-                  "ToolVersion": "1.0"
-              }
-          ]
-    - task: PublishBuildArtifacts@1
-      displayName: Publish Artifact (nupkg)
-      inputs:
-        PathtoPublish: $(Build.ArtifactStagingDirectory)\nupkg
-        ArtifactName: conpty-nupkg-$(BuildConfiguration)
-
-
-- ${{ if eq(parameters.buildWPF, true) }}:
-  - job: PackageAndSignWPF
-    strategy:
-      matrix:
-        ${{ each config in parameters.buildConfigurations }}:
-          ${{ config }}:
-            BuildConfiguration: ${{ config }}
-    displayName: Create NuGet Package (WPF Terminal Control)
-    dependsOn: Build
-    steps:
-    - checkout: self
-      clean: true
-      fetchDepth: 1
-      submodules: true
-      persistCredentials: True
-    - task: PkgESSetupBuild@12
-      displayName: Package ES - Setup Build
-      inputs:
-        disableOutputRedirect: true
-    - ${{ each platform in parameters.buildPlatforms }}:
-      - task: DownloadBuildArtifacts@1
-        displayName: Download ${{ platform }} PublicTerminalCore
-        inputs:
-          artifactName: wpf-dll-${{ platform }}-$(BuildConfiguration)
-          itemPattern: '**/*.dll'
-          downloadPath: bin\${{ platform }}\$(BuildConfiguration)\
-          extractTars: false
-    - task: PowerShell@2
-      displayName: Move downloaded artifacts around
-      inputs:
-        targetType: inline
-        # Find all artifact files and move them up a directory. Ugh.
-        script: |-
-          Get-ChildItem bin -Recurse -Directory -Filter wpf-dll-* | % {
-            $_ | Get-ChildItem -Recurse -File | % {
-              Move-Item -Verbose $_.FullName $_.Directory.Parent.FullName
-            }
-          }
-          Move-Item bin\x86 bin\Win32
-    - task: NuGetToolInstaller@1
-      displayName: Use NuGet 5.10.0
-      inputs:
-        versionSpec: 5.10.0
-    - task: NuGetCommand@2
-      displayName: NuGet restore copy
-      inputs:
-        selectOrConfig: config
-        nugetConfigPath: NuGet.Config
-    - task: VSBuild@1
-      displayName: Build solution **\OpenConsole.sln for WPF Control
-      inputs:
-        solution: '**\OpenConsole.sln'
-        msbuildArgs: /p:WindowsTerminalReleaseBuild=$(UseReleaseBranding);Version=$(XES_PACKAGEVERSIONNUMBER) /t:Pack
-        platform: Any CPU
-        configuration: $(BuildConfiguration)
-        maximumCpuCount: true
-    - task: PublishSymbols@2
-      displayName: Publish symbols path
-      continueOnError: True
-      inputs:
-        SearchPattern: |
-          $(Build.SourcesDirectory)/bin/**/*.pdb
-          $(Build.SourcesDirectory)/bin/**/*.exe
-          $(Build.SourcesDirectory)/bin/**/*.dll
-        IndexSources: false
-        SymbolServerType: TeamServices
-        SymbolsArtifactName: Symbols_WPF_$(BuildConfiguration)
-    - task: CopyFiles@2
-      displayName: Copy *.nupkg to Artifacts
-      inputs:
-        Contents: '**/*Wpf*.nupkg'
-        TargetFolder: $(Build.ArtifactStagingDirectory)/nupkg
-        OverWrite: true
-        flattenFolders: true
-    - task: EsrpCodeSigning@1
-      displayName: Submit *.nupkg to ESRP for code signing
-      inputs:
-        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
-        FolderPath: $(Build.ArtifactStagingDirectory)/nupkg
-        Pattern: '*.nupkg'
-        UseMinimatch: true
-        signConfigType: inlineSignParams
-        inlineOperation: >-
-          [
-              {
-                  "KeyCode": "CP-401405",
-                  "OperationCode": "NuGetSign",
-                  "Parameters": {},
-                  "ToolName": "sign",
-                  "ToolVersion": "1.0"
-              },
-              {
-                  "KeyCode": "CP-401405",
-                  "OperationCode": "NuGetVerify",
-                  "Parameters": {},
-                  "ToolName": "sign",
-                  "ToolVersion": "1.0"
-              }
-          ]
-    - task: PublishBuildArtifacts@1
-      displayName: Publish Artifact (nupkg)
-      inputs:
-        PathtoPublish: $(Build.ArtifactStagingDirectory)\nupkg
-        ArtifactName: wpf-nupkg-$(BuildConfiguration)
-
-- ${{ if eq(parameters.publishSymbolsToPublic, true) }}:
-  - job: PublishSymbols
-    displayName: Publish Symbols
-    dependsOn: BundleAndSign
-    steps:
-    - checkout: self
-      clean: true
-      fetchDepth: 1
-      submodules: true
-    - task: PkgESSetupBuild@12
-      displayName: Package ES - Setup Build
-
-    - template: .\templates\restore-nuget-steps.yml
-
-    # Download the terminal-PLATFORM-CONFIG-VERSION artifact for every platform/version combo
-    - ${{ each platform in parameters.buildPlatforms }}:
-      - task: DownloadBuildArtifacts@1
-        displayName: Download Symbols ${{ platform }}
-        inputs:
-          artifactName: terminal-${{ platform }}-Release
-          itemPattern: '**/*.appxsym'
-
-    # It seems easier to do this -- download every appxsym -- then enumerate all the PDBs in the build directory for the
-    # public symbol push. Otherwise, we would have to list all of the PDB files one by one.
-    - pwsh: |-
-        mkdir $(Build.SourcesDirectory)/appxsym-temp
-        Get-ChildItem "$(System.ArtifactsDirectory)" -Filter *.appxsym -Recurse | % {
-          $src = $_.FullName
-          $dest = Join-Path "$(Build.SourcesDirectory)/appxsym-temp/" $_.Name
-
-          mkdir $dest
-          Write-Host "Extracting $src to $dest..."
-          tar -x -v -f $src -C $dest
-        }
-      displayName: Extract symbols for public consumption
-
-    - task: PowerShell@2
-      displayName: Source Index PDBs (the public ones)
-      inputs:
-        filePath: build\scripts\Index-Pdbs.ps1
-        arguments: -SearchDir '$(Build.SourcesDirectory)/appxsym-temp' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
-        pwsh: true
-
-    # Publish the app symbols to the public MSDL symbol server
-    # accessible via https://msdl.microsoft.com/download/symbols
-    - task: PublishSymbols@2
-      displayName: 'Publish app symbols to MSDL'
-      inputs:
-        symbolsFolder: '$(Build.SourcesDirectory)/appxsym-temp'
-        searchPattern: '**/*.pdb'
-        SymbolsMaximumWaitTime: 30
-        SymbolServerType: 'TeamServices'
-        SymbolsProduct: 'Windows Terminal Application Binaries'
-        SymbolsVersion: '$(XES_APPXMANIFESTVERSION)'
-        # The ADO task does not support indexing of GitHub sources.
-        indexSources: false
-        detailedLog: true
-      # There is a bug which causes this task to fail if LIB includes an inaccessible path (even though it does not depend on it).
-      # To work around this issue, we just force LIB to be any dir that we know exists.
-      # Copied from https://github.com/microsoft/icu/blob/f869c214adc87415dfe751d81f42f1bca55dcf5f/build/azure-nuget.yml#L564-L583
-      env:
-        LIB: $(Build.SourcesDirectory)
-        ArtifactServices_Symbol_AccountName: microsoftpublicsymbols
-        ArtifactServices_Symbol_PAT: $(ADO_microsoftpublicsymbols_PAT)
-
-
-- ${{ if eq(parameters.buildTerminalVPack, true) }}:
-  - job: VPack
-    displayName: Create Windows vPack
-    dependsOn: BundleAndSign
-    steps:
-    - checkout: self
-      clean: true
-      fetchDepth: 1
-      submodules: true
-    - task: PkgESSetupBuild@12
-      displayName: Package ES - Setup Build
-    - task: DownloadBuildArtifacts@1
-      displayName: Download Build Artifacts
-      inputs:
-        artifactName: appxbundle-signed
-        extractTars: false
-    - task: PowerShell@2
-      displayName: Rename and stage packages for vpack
-      inputs:
-        targetType: inline
-        script: >-
-          # Rename to known/fixed name for Windows build system
-
-          Get-ChildItem Microsoft.WindowsTerminal_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' }
-
-
-          # Create vpack directory and place item inside
 
-          mkdir WindowsTerminal.app
+stages:
+  - stage: Build
+    displayName: Build
+    dependsOn: []
+    jobs:
+      - template: ./templates-v2/job-build-project.yml
+        parameters:
+          pool:
+            name: SHINE-INT-L # Run the compilation on the large agent pool, rather than the default small one.
+            demands: ImageOverride -equals SHINE-VS17-Latest
+          branding: ${{ parameters.branding }}
+          buildTerminal: ${{ parameters.buildTerminal }}
+          buildConPTY: ${{ parameters.buildConPTY }}
+          buildWPF: ${{ parameters.buildWPF }}
+          pgoBuildMode: ${{ parameters.pgoBuildMode }}
+          buildConfigurations: ${{ parameters.buildConfigurations }}
+          buildPlatforms: ${{ parameters.buildPlatforms }}
+          generateSbom: ${{ parameters.generateSbom }}
+          codeSign: ${{ parameters.codeSign }}
+          beforeBuildSteps: # Right before we build, lay down the universal package and localizations
+            - task: PkgESSetupBuild@12
+              displayName: Package ES - Setup Build
+              inputs:
+                disableOutputRedirect: true
+
+            - task: UniversalPackages@0
+              displayName: Download terminal-internal Universal Package
+              inputs:
+                feedListDownload: 2b3f8893-a6e8-411f-b197-a9e05576da48
+                packageListDownload: e82d490c-af86-4733-9dc4-07b772033204
+                versionListDownload: ${{ parameters.terminalInternalPackageVersion }}
+
+            - template: ./templates-v2/steps-fetch-and-prepare-localizations.yml
+              parameters:
+                includePseudoLoc: true
+
+      - ${{ if eq(parameters.buildWPF, true) }}:
+        # Add an Any CPU build flavor for the WPF control bits
+        - template: ./templates-v2/job-build-project.yml
+          parameters:
+            # This job is allowed to run on the default small pool.
+            jobName: BuildWPF
+            branding: ${{ parameters.branding }}
+            buildTerminal: false
+            buildWPFDotNetComponents: true
+            buildConfigurations: ${{ parameters.buildConfigurations }}
+            buildPlatforms:
+              - Any CPU
+            generateSbom: ${{ parameters.generateSbom }}
+            codeSign: ${{ parameters.codeSign }}
+            beforeBuildSteps:
+              - task: PkgESSetupBuild@12
+                displayName: Package ES - Setup Build
+                inputs:
+                  disableOutputRedirect: true
+              # WPF doesn't need the localizations or the universal package, but if it does... put them here.
+
+  - stage: Package
+    displayName: Package
+    dependsOn: [Build]
+    jobs:
+      - ${{ if eq(parameters.buildTerminal, true) }}:
+        - template: ./templates-v2/job-merge-msix-into-bundle.yml
+          parameters:
+            jobName: Bundle
+            branding: ${{ parameters.branding }}
+            buildConfigurations: ${{ parameters.buildConfigurations }}
+            buildPlatforms: ${{ parameters.buildPlatforms }}
+            generateSbom: ${{ parameters.generateSbom }}
+            codeSign: ${{ parameters.codeSign }}
+
+      - ${{ if eq(parameters.buildConPTY, true) }}:
+        - template: ./templates-v2/job-package-conpty.yml
+          parameters:
+            buildConfigurations: ${{ parameters.buildConfigurations }}
+            buildPlatforms: ${{ parameters.buildPlatforms }}
+            generateSbom: ${{ parameters.generateSbom }}
+            codeSign: ${{ parameters.codeSign }}
+
+      - ${{ if eq(parameters.buildWPF, true) }}:
+        - template: ./templates-v2/job-build-package-wpf.yml
+          parameters:
+            buildConfigurations: ${{ parameters.buildConfigurations }}
+            buildPlatforms: ${{ parameters.buildPlatforms }}
+            generateSbom: ${{ parameters.generateSbom }}
+            codeSign: ${{ parameters.codeSign }}
+
+  - stage: Publish
+    displayName: Publish
+    dependsOn: [Build, Package]
+    jobs:
+      # We only support the vpack for Release builds that include Terminal
+      - ${{ if and(containsValue(parameters.buildConfigurations, 'Release'), parameters.buildTerminal, parameters.publishVpackToWindows) }}:
+        - template: ./templates-v2/job-submit-windows-vpack.yml
+          parameters:
+            buildConfiguration: Release
+            generateSbom: ${{ parameters.generateSbom }}
+
+      - template: ./templates-v2/job-publish-symbols.yml
+        parameters:
+          includePublicSymbolServer: ${{ parameters.publishSymbolsToPublic }}
 
-          mv Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle .\WindowsTerminal.app\
-        workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed
-    - task: PkgESVPack@12
-      displayName: 'Package ES - VPack'
-      env:
-        SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-      inputs:
-        sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed\WindowsTerminal.app
-        description: VPack for the Windows Terminal Application
-        pushPkgName: WindowsTerminal.app
-        owner: conhost
-        githubToken: $(GitHubTokenForVpackProvenance)
-    - task: PublishPipelineArtifact@1
-      displayName: 'Copy VPack Manifest to Drop'
-      inputs:
-        targetPath: $(XES_VPACKMANIFESTDIRECTORY)
-        artifactName: VPackManifest
-    - task: PkgESFCIBGit@12
-      displayName: 'Submit VPack Manifest to Windows'
-      inputs:
-        configPath: '$(Build.SourcesDirectory)\build\config\GitCheckin.json'
-        artifactsDirectory: $(XES_VPACKMANIFESTDIRECTORY)
-        prTimeOut: 5
 ...
diff --git a/build/pipelines/templates-v2/job-build-package-wpf.yml b/build/pipelines/templates-v2/job-build-package-wpf.yml
new file mode 100644
index 00000000000..fe668058b20
--- /dev/null
+++ b/build/pipelines/templates-v2/job-build-package-wpf.yml
@@ -0,0 +1,134 @@
+parameters:
+  - name: buildConfigurations
+    type: object
+  - name: buildPlatforms
+    type: object
+  - name: generateSbom
+    type: boolean
+    default: false
+  - name: codeSign
+    type: boolean
+    default: false
+  - name: pool
+    type: object
+    default: []
+  - name: dependsOn
+    type: object
+    default: null
+  - name: artifactStem
+    type: string
+    default: ''
+  - name: jobName
+    type: string
+    default: PackWPF
+
+jobs:
+- job: ${{ parameters.jobName }}
+  ${{ if ne(length(parameters.pool), 0) }}:
+    pool: ${{ parameters.pool }}
+  ${{ if eq(parameters.codeSign, true) }}:
+    displayName: Pack and Sign Microsoft.Terminal.Wpf
+  ${{ else }}:
+    displayName: Pack Microsoft.Terminal.Wpf
+  strategy:
+    matrix:
+      ${{ each config in parameters.buildConfigurations }}:
+        ${{ config }}:
+          BuildConfiguration: ${{ config }}
+  dependsOn: ${{ parameters.dependsOn }}
+  variables:
+    OutputBuildPlatform: AnyCPU
+    Terminal.BinDir: $(Build.SourcesDirectory)/bin/$(OutputBuildPlatform)/$(BuildConfiguration)
+  steps:
+  - checkout: self
+    clean: true
+    fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
+    submodules: true
+    persistCredentials: True
+
+  - task: PkgESSetupBuild@12
+    displayName: Package ES - Setup Build
+    inputs:
+      disableOutputRedirect: true
+
+  - template: steps-download-bin-dir-artifact.yml
+    parameters:
+      buildPlatforms:
+        - ${{ parameters.buildPlatforms }}
+        - Any CPU # Make sure we grab the precompiled WPF bits
+      # This build is already matrix'd on configuration, so
+      # just pass a single config into the download template.
+      buildConfigurations:
+        - $(BuildConfiguration)
+      artifactStem: ${{ parameters.artifactStem }}
+
+  - template: .\steps-restore-nuget.yml
+
+  - task: VSBuild@1
+    displayName: Build solution OpenConsole.sln for WPF Control (Pack)
+    inputs:
+      solution: 'OpenConsole.sln'
+      msbuildArgs: >-
+        /p:WindowsTerminalReleaseBuild=true;Version=$(XES_PACKAGEVERSIONNUMBER)
+        /p:NoBuild=true
+        /p:IncludeSymbols=true
+        /t:Terminal\wpf\WpfTerminalControl:Pack
+      platform: Any CPU
+      configuration: $(BuildConfiguration)
+      maximumCpuCount: true
+      clean: false
+
+  - task: CopyFiles@2
+    displayName: Copy *.nupkg to Artifacts
+    inputs:
+      Contents: 'bin/**/*Wpf*.nupkg'
+      TargetFolder: $(Build.ArtifactStagingDirectory)/nupkg
+      OverWrite: true
+      flattenFolders: true
+
+  - ${{ if eq(parameters.codeSign, true) }}:
+    - task: EsrpCodeSigning@1
+      displayName: Submit *.nupkg to ESRP for code signing
+      inputs:
+        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
+        FolderPath: $(Build.ArtifactStagingDirectory)/nupkg
+        Pattern: '*.nupkg'
+        UseMinimatch: true
+        signConfigType: inlineSignParams
+        inlineOperation: >-
+          [
+              {
+                  "KeyCode": "CP-401405",
+                  "OperationCode": "NuGetSign",
+                  "Parameters": {},
+                  "ToolName": "sign",
+                  "ToolVersion": "1.0"
+              },
+              {
+                  "KeyCode": "CP-401405",
+                  "OperationCode": "NuGetVerify",
+                  "Parameters": {},
+                  "ToolName": "sign",
+                  "ToolVersion": "1.0"
+              }
+          ]
+
+  - ${{ if eq(parameters.generateSbom, true) }}:
+    - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
+      displayName: 'Generate SBOM manifest (wpf)'
+      inputs:
+       BuildDropPath: '$(System.ArtifactsDirectory)/nupkg'
+       BuildComponentPath: '$(Build.SourcesDirectory)/bin'
+
+    - task: DropValidatorTask@0
+      displayName: 'Validate wpf SBOM manifest'
+      inputs:
+        BuildDropPath: '$(System.ArtifactsDirectory)/nupkg'
+        OutputPath: 'output.json'
+        ValidateSignature: true
+        Verbosity: 'Verbose'
+
+  - publish: $(Build.ArtifactStagingDirectory)\nupkg
+    artifact: wpf-nupkg-$(BuildConfiguration)${{ parameters.artifactStem }}
+    displayName: Publish nupkg
diff --git a/build/pipelines/templates-v2/job-build-project.yml b/build/pipelines/templates-v2/job-build-project.yml
new file mode 100644
index 00000000000..1c07bb9008e
--- /dev/null
+++ b/build/pipelines/templates-v2/job-build-project.yml
@@ -0,0 +1,263 @@
+parameters:
+  - name: branding
+    type: string
+    default: Dev
+    values: [Release, Preview, Dev]
+  - name: additionalBuildOptions
+    type: string
+    default: ''
+  - name: buildTerminal
+    type: boolean
+    default: true
+  - name: buildConPTY
+    type: boolean
+    default: false
+  - name: buildWPF
+    type: boolean
+    default: false
+  - name: buildWPFDotNetComponents # This weird hack is to make sure we sign and source index the .NET pieces
+    type: boolean
+    default: false
+  - name: buildEverything
+    displayName: "Build Everything (Overrides all other build options)"
+    type: boolean
+    default: false
+  - name: pgoBuildMode
+    type: string
+    default: None
+    values: [Optimize, Instrument, None]
+  - name: buildConfigurations
+    type: object
+    default:
+      - Release
+  - name: buildPlatforms
+    type: object
+    default:
+      - x64
+      - x86
+      - arm64
+  - name: generateSbom
+    type: boolean
+    default: false
+  - name: codeSign
+    type: boolean
+    default: false
+  - name: keepAllExpensiveBuildOutputs
+    type: boolean
+    default: true
+  - name: artifactStem
+    type: string
+    default: ''
+  - name: jobName
+    type: string
+    default: 'Build'
+  - name: pool
+    type: object
+    default: []
+  - name: beforeBuildSteps
+    type: stepList
+    default: []
+
+jobs:
+- job: ${{ parameters.jobName }}
+  ${{ if ne(length(parameters.pool), 0) }}:
+    pool: ${{ parameters.pool }}
+  strategy:
+    matrix:
+      ${{ each config in parameters.buildConfigurations }}:
+        ${{ each platform in parameters.buildPlatforms }}:
+          ${{ config }}_${{ platform }}:
+            BuildConfiguration: ${{ config }}
+            BuildPlatform: ${{ platform }}
+            ${{ if eq(platform, 'x86') }}:
+              OutputBuildPlatform: Win32
+            ${{ elseif eq(platform, 'Any CPU') }}:
+              OutputBuildPlatform: AnyCPU
+            ${{ else }}:
+              OutputBuildPlatform: ${{ platform }}
+  variables:
+    MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe'
+    Terminal.BinDir: $(Build.SourcesDirectory)/bin/$(OutputBuildPlatform)/$(BuildConfiguration)
+    # Azure DevOps abhors a vacuum
+    # If these are blank, expansion will fail later on... which will result in direct substitution of the variable *names*
+    # later on. We'll just... set them to a single space and if we need to, check IsNullOrWhiteSpace.
+    # Yup.
+    BuildTargetParameter: ' '
+    SelectedSigningFragments: ' '
+  displayName: Build
+  timeoutInMinutes: 240
+  cancelTimeoutInMinutes: 1
+  steps:
+  - checkout: self
+    clean: true
+    submodules: true
+    persistCredentials: True
+  # This generates either nothing for BuildTargetParameter, or /t:X;Y;Z, to control targets later.
+  - pwsh: |-
+      If (-Not [bool]::Parse("${{ parameters.buildEverything }}")) {
+        $BuildTargets = @()
+        $SignFragments = @()
+        If ([bool]::Parse("${{ parameters.buildTerminal }}")) {
+          $BuildTargets += "Terminal\CascadiaPackage"
+          $SignFragments += "terminal_constituents"
+        }
+        If ([bool]::Parse("${{ parameters.buildWPFDotNetComponents }}")) {
+          $BuildTargets += "Terminal\wpf\WpfTerminalControl"
+          $SignFragments += "wpfdotnet"
+        }
+        If ([bool]::Parse("${{ parameters.buildWPF }}")) {
+          $BuildTargets += "Terminal\wpf\PublicTerminalCore"
+          $SignFragments += "wpf"
+        }
+        If ([bool]::Parse("${{ parameters.buildConPTY }}")) {
+          $BuildTargets += "Conhost\Host_EXE;Conhost\winconpty_DLL"
+          $SignFragments += "conpty"
+        }
+        Write-Host "Targets: $($BuildTargets -Join ";")"
+        Write-Host "Sign targets: $($SignFragments -Join ";")"
+        Write-Host "##vso[task.setvariable variable=BuildTargetParameter]/t:$($BuildTargets -Join ";")"
+        Write-Host "##vso[task.setvariable variable=SelectedSigningFragments]$($SignFragments -Join ";")"
+      }
+    displayName: Prepare Build and Sign Targets
+
+  - pwsh: |-
+      .\build\scripts\Generate-ThirdPartyNotices.ps1 -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
+    displayName: Generate NOTICE.html from NOTICE.md
+
+  - template: .\steps-restore-nuget.yml
+
+  - ${{ parameters.beforeBuildSteps }}
+
+  - task: VSBuild@1
+    displayName: Build OpenConsole.sln
+    inputs:
+      solution: 'OpenConsole.sln'
+      msbuildArgs: >-
+        /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }}
+        ${{ parameters.additionalBuildOptions }}
+        /bl:$(Build.SourcesDirectory)\msbuild.binlog
+        $(BuildTargetParameter)
+      platform: $(BuildPlatform)
+      configuration: $(BuildConfiguration)
+      maximumCpuCount: true
+
+  - publish: $(Build.SourcesDirectory)/msbuild.binlog
+    artifact: logs-$(BuildPlatform)-$(BuildConfiguration)${{ parameters.artifactStem }}
+    condition: always()
+    displayName: Publish Build Log
+
+  # This saves ~2GiB per architecture. We won't need these later.
+  # Removes:
+  # - All .lib that do not have an associated .exp (which would indicate that they are import libs)
+  # - All .pdbs from those .libs (which were only used during linking)
+  # - Directories ending in Lib (static lib projects that we fully linked into DLLs which may also contain unnecessary resources)
+  # - All LocalTests_ project outputs, as they were subsumed into TestHostApp
+  # - All PDB files inside the WindowsTerminal/ output, which do not belong there.
+  - pwsh: |-
+      $binDir = '$(Terminal.BinDir)'
+      $ImportLibs = Get-ChildItem $binDir -Recurse -File -Filter '*.exp' | ForEach-Object { $_.FullName -Replace "exp$","lib" }
+      $StaticLibs = Get-ChildItem $binDir -Recurse -File -Filter '*.lib' | Where-Object FullName -NotIn $ImportLibs
+
+      $Items = @()
+      $Items += $StaticLibs
+      $Items += Get-Item ($StaticLibs.FullName -Replace "lib$","pdb") -ErrorAction:Ignore
+      $Items += Get-ChildItem $binDir -Directory -Filter '*Lib'
+      $Items += Get-ChildItem $binDir -Directory -Filter 'LocalTests_*'
+      $Items += Get-ChildItem "${$binDir}\WindowsTerminal" -Filter '*.pdb' -ErrorAction:Ignore
+
+      If (-Not [bool]::Parse('${{ parameters.keepAllExpensiveBuildOutputs }}')) {
+        $Items += Get-ChildItem '$(Terminal.BinDir)' -Filter '*.pdb' -Recurse
+      }
+
+      $Items | Remove-Item -Recurse -Force -Verbose -ErrorAction:Ignore
+    displayName: Clean up static libs and extra symbols
+    errorActionPreference: silentlyContinue # It's OK if this silently fails
+
+  # We cannot index PDBs that we have deleted!
+  - ${{ if eq(parameters.keepAllExpensiveBuildOutputs, true) }}:
+    - pwsh: |-
+        build\scripts\Index-Pdbs.ps1 -SearchDir '$(Terminal.BinDir)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
+      displayName: Source Index PDBs
+      errorActionPreference: silentlyContinue
+
+  - ${{ if or(parameters.buildTerminal, parameters.buildEverything) }}:
+    - pwsh: |-
+        $Package = (Get-ChildItem -Recurse -Filter "CascadiaPackage*.msix" | Select -First 1)
+        $PackageFilename = $Package.FullName
+        Write-Host "##vso[task.setvariable variable=WindowsTerminalPackagePath]${PackageFilename}"
+      displayName: Locate the MSIX
+
+    # CHECK EXCEPTION
+    # PGO requires a desktop CRT
+    - ${{ if ne(parameters.pgoBuildMode, 'Instrument') }}:
+      - pwsh: |-
+          .\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path "$(WindowsTerminalPackagePath)"
+        displayName: Check MSIX for common regressions
+        condition: and(succeeded(), ne(variables.WindowsTerminalPackagePath, ''))
+      
+    - ${{ if eq(parameters.codeSign, true) }}:
+      - pwsh: |-
+          & "$(MakeAppxPath)" unpack /p "$(WindowsTerminalPackagePath)" /d "$(Terminal.BinDir)/PackageContents"
+        displayName: Unpack the MSIX for signing
+
+  - ${{ if eq(parameters.codeSign, true) }}:
+    - template: steps-create-signing-config.yml
+      parameters:
+        outFile: '$(Build.SourcesDirectory)/ESRPSigningConfig.json'
+        stage: build
+        fragments: $(SelectedSigningFragments)
+
+    # Code-sign everything we just put together.
+    # We run the signing in Terminal.BinDir, because all of the signing batches are relative to the final architecture/configuration output folder.
+    - task: EsrpCodeSigning@1
+      displayName: Submit Signing Request
+      inputs:
+        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
+        FolderPath: '$(Terminal.BinDir)'
+        signType: batchSigning
+        batchSignPolicyFile: '$(Build.SourcesDirectory)/ESRPSigningConfig.json'
+
+    # We only need to re-pack the MSIX if we actually signed, so this can stay in the codeSign conditional
+    - ${{ if or(parameters.buildTerminal, parameters.buildEverything) }}:
+      - pwsh: |-
+          $outDir = New-Item -Type Directory "$(Terminal.BinDir)/_appx" -ErrorAction:Ignore
+          $PackageFilename = Join-Path $outDir.FullName (Split-Path -Leaf "$(WindowsTerminalPackagePath)")
+          & "$(MakeAppxPath)" pack /h SHA256 /o /p $PackageFilename /d "$(Terminal.BinDir)/PackageContents"
+          Write-Host "##vso[task.setvariable variable=WindowsTerminalPackagePath]${PackageFilename}"
+        displayName: Re-pack the new Terminal package after signing
+
+  - ${{ else }}: # No Signing
+    - ${{ if or(parameters.buildTerminal, parameters.buildEverything) }}:
+      - pwsh: |-
+          $outDir = New-Item -Type Directory "$(Terminal.BinDir)/_appx" -ErrorAction:Ignore
+          $PackageFilename = Join-Path $outDir.FullName (Split-Path -Leaf "$(WindowsTerminalPackagePath)")
+          Copy-Item "$(WindowsTerminalPackagePath)" $PackageFilename
+          Write-Host "##vso[task.setvariable variable=WindowsTerminalPackagePath]${PackageFilename}"
+        displayName: Stage the package (unsigned)
+        condition: and(succeeded(), ne(variables.WindowsTerminalPackagePath, ''))
+
+  - ${{ if or(parameters.buildTerminal, parameters.buildEverything) }}:
+    - pwsh: |-
+        $XamlAppxPath = (Get-Item "src\cascadia\CascadiaPackage\AppPackages\*\Dependencies\$(BuildPlatform)\Microsoft.UI.Xaml*.appx").FullName
+        $outDir = New-Item -Type Directory "$(Terminal.BinDir)/_unpackaged" -ErrorAction:Ignore
+        & .\build\scripts\New-UnpackagedTerminalDistribution.ps1 -TerminalAppX $(WindowsTerminalPackagePath) -XamlAppX $XamlAppxPath -Destination $outDir.FullName
+      displayName: Build Unpackaged Distribution (from MSIX)
+      condition: and(succeeded(), ne(variables.WindowsTerminalPackagePath, ''))
+
+  - ${{ if eq(parameters.generateSbom, true) }}:
+    - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
+      displayName: 'Generate SBOM manifest'
+      inputs:
+        BuildDropPath: '$(Terminal.BinDir)'
+
+    - task: DropValidatorTask@0
+      displayName: 'Validate SBOM manifest'
+      inputs:
+        BuildDropPath: '$(Terminal.BinDir)'
+        OutputPath: 'output.json'
+        ValidateSignature: true
+        Verbosity: 'Verbose'
+
+  - publish: $(Terminal.BinDir)
+    artifact: build-$(BuildPlatform)-$(BuildConfiguration)${{ parameters.artifactStem }}
+    displayName: Publish All Outputs
diff --git a/build/pipelines/templates-v2/job-check-code-format.yml b/build/pipelines/templates-v2/job-check-code-format.yml
new file mode 100644
index 00000000000..e889fc8756d
--- /dev/null
+++ b/build/pipelines/templates-v2/job-check-code-format.yml
@@ -0,0 +1,15 @@
+jobs:
+- job: CodeFormatCheck
+  displayName: Check Code Format
+  pool: { vmImage: windows-2022 }
+
+  steps:
+  - checkout: self
+    fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
+    submodules: false
+    clean: true
+
+  - powershell: |-
+      .\build\scripts\Invoke-FormattingCheck.ps1
+    displayName: 'Run formatters'
diff --git a/build/pipelines/templates/codenav-indexer.yml b/build/pipelines/templates-v2/job-index-github-codenav.yml
similarity index 57%
rename from build/pipelines/templates/codenav-indexer.yml
rename to build/pipelines/templates-v2/job-index-github-codenav.yml
index 04e018ed2a0..e2edf55e651 100644
--- a/build/pipelines/templates/codenav-indexer.yml
+++ b/build/pipelines/templates-v2/job-index-github-codenav.yml
@@ -1,21 +1,15 @@
-parameters:
-  artifactName: 'drop'
-
 jobs:
 - job: CodeNavIndexer
   displayName: Run Github CodeNav Indexer
-  pool: { vmImage: windows-2019 }
+  pool: { vmImage: windows-2022 }
 
   steps:
   - checkout: self
     fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
     submodules: false
     clean: true
 
-  - task: DownloadBuildArtifacts@0
-    inputs:
-      artifactName: ${{ parameters.artifactName }}
-
   - task: RichCodeNavIndexer@0
     inputs:
       languages: 'cpp,csharp'
diff --git a/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml b/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml
new file mode 100644
index 00000000000..318d3b2673a
--- /dev/null
+++ b/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml
@@ -0,0 +1,133 @@
+parameters:
+  - name: branding
+    type: string
+  - name: buildConfigurations
+    type: object
+  - name: buildPlatforms
+    type: object
+  - name: generateSbom
+    type: boolean
+    default: false
+  - name: codeSign
+    type: boolean
+    default: false
+  - name: pool
+    type: object
+    default: []
+  - name: dependsOn
+    type: object
+    default: null
+  - name: artifactStem
+    type: string
+    default: ''
+  - name: jobName
+    type: string
+    default: Bundle
+
+jobs:
+- job: ${{ parameters.jobName }}
+  ${{ if ne(length(parameters.pool), 0) }}:
+    pool: ${{ parameters.pool }}
+  ${{ if eq(parameters.codeSign, true) }}:
+    displayName: Pack and Sign Terminal MSIXBundle
+  ${{ else }}:
+    displayName: Pack Terminal MSIXBundle
+  strategy:
+    matrix:
+      ${{ each config in parameters.buildConfigurations }}:
+        ${{ config }}:
+          BuildConfiguration: ${{ config }}
+  variables:
+    ${{ if eq(parameters.branding, 'Release') }}:
+      BundleStemName: Microsoft.WindowsTerminal
+    ${{ elseif eq(parameters.branding, 'Preview') }}:
+      BundleStemName: Microsoft.WindowsTerminalPreview
+    ${{ else }}:
+      BundleStemName: WindowsTerminalDev
+  dependsOn: ${{ parameters.dependsOn }}
+  steps:
+  - checkout: self
+    clean: true
+    fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
+    submodules: true
+    persistCredentials: True
+  - task: PkgESSetupBuild@12
+    displayName: Package ES - Setup Build
+    inputs:
+      disableOutputRedirect: true
+  - template: steps-download-bin-dir-artifact.yml
+    parameters:
+      buildPlatforms: ${{ parameters.buildPlatforms }}
+      # This build is already matrix'd on configuration, so
+      # just pass a single config into the download template.
+      buildConfigurations:
+        - $(BuildConfiguration)
+      artifactStem: ${{ parameters.artifactStem }}
+
+  # Add 3000 to the major version component, but only for the bundle.
+  # This is to ensure that it is newer than "2022.xx.yy.zz" or whatever the original bundle versions were before
+  # we switched to uniform naming.
+  - pwsh: |-
+      $VersionEpoch = 3000
+      $Components = "$(XES_APPXMANIFESTVERSION)" -Split "\."
+      $Components[0] = ([int]$Components[0] + $VersionEpoch)
+      $BundleVersion = $Components -Join "."
+      New-Item -Type Directory "$(System.ArtifactsDirectory)/bundle"
+      .\build\scripts\Create-AppxBundle.ps1 -InputPath 'bin/' -ProjectName CascadiaPackage -BundleVersion $BundleVersion -OutputPath "$(System.ArtifactsDirectory)\bundle\$(BundleStemName)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
+    displayName: Create msixbundle
+
+  - ${{ if eq(parameters.codeSign, true) }}:
+    - task: EsrpCodeSigning@1
+      displayName: Submit *.msixbundle to ESRP for code signing
+      inputs:
+        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
+        FolderPath: $(System.ArtifactsDirectory)\bundle
+        Pattern: $(BundleStemName)*.msixbundle
+        UseMinimatch: true
+        signConfigType: inlineSignParams
+        inlineOperation: >-
+          [
+              {
+                  "KeyCode": "Dynamic",
+                  "CertTemplateName": "WINMSAPP1ST",
+                  "CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
+                  "OperationCode": "SigntoolSign",
+                  "Parameters": {
+                      "OpusName": "Microsoft",
+                      "OpusInfo": "http://www.microsoft.com",
+                      "FileDigest": "/fd \"SHA256\"",
+                      "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+                  },
+                  "ToolName": "sign",
+                  "ToolVersion": "1.0"
+              },
+              {
+                  "KeyCode": "Dynamic",
+                  "CertTemplateName": "WINMSAPP1ST",
+                  "CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
+                  "OperationCode": "SigntoolVerify",
+                  "Parameters": {},
+                  "ToolName": "sign",
+                  "ToolVersion": "1.0"
+              }
+          ]
+
+  - ${{ if eq(parameters.generateSbom, true) }}:
+    - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
+      displayName: 'Generate SBOM manifest (bundle)'
+      inputs:
+       BuildDropPath: '$(System.ArtifactsDirectory)/bundle'
+       BuildComponentPath: '$(Build.SourcesDirectory)/bin'
+
+    - task: DropValidatorTask@0
+      displayName: 'Validate bundle SBOM manifest'
+      inputs:
+        BuildDropPath: '$(System.ArtifactsDirectory)/bundle'
+        OutputPath: 'output.json'
+        ValidateSignature: true
+        Verbosity: 'Verbose'
+
+  - publish: '$(System.ArtifactsDirectory)/bundle'
+    artifact: appxbundle-$(BuildConfiguration)${{ parameters.artifactStem }}
+    displayName: Publish msixbundle
diff --git a/build/pipelines/templates-v2/job-package-conpty.yml b/build/pipelines/templates-v2/job-package-conpty.yml
new file mode 100644
index 00000000000..db648d905f2
--- /dev/null
+++ b/build/pipelines/templates-v2/job-package-conpty.yml
@@ -0,0 +1,118 @@
+parameters:
+  - name: buildConfigurations
+    type: object
+  - name: buildPlatforms
+    type: object
+  - name: generateSbom
+    type: boolean
+    default: false
+  - name: codeSign
+    type: boolean
+    default: false
+  - name: pool
+    type: object
+    default: []
+  - name: dependsOn
+    type: object
+    default: null
+  - name: artifactStem
+    type: string
+    default: ''
+  - name: jobName
+    type: string
+    default: PackConPTY
+
+jobs:
+- job: ${{ parameters.jobName }}
+  ${{ if ne(length(parameters.pool), 0) }}:
+    pool: ${{ parameters.pool }}
+  ${{ if eq(parameters.codeSign, true) }}:
+    displayName: Pack and Sign Microsoft.Windows.Console.ConPTY
+  ${{ else }}:
+    displayName: Pack Microsoft.Windows.Console.ConPTY
+  strategy:
+    matrix:
+      ${{ each config in parameters.buildConfigurations }}:
+        ${{ config }}:
+          BuildConfiguration: ${{ config }}
+  dependsOn: ${{ parameters.dependsOn }}
+  steps:
+  - checkout: self
+    clean: true
+    fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
+    submodules: true
+    persistCredentials: True
+
+  - task: PkgESSetupBuild@12
+    displayName: Package ES - Setup Build
+    inputs:
+      disableOutputRedirect: true
+
+  - template: steps-download-bin-dir-artifact.yml
+    parameters:
+      buildPlatforms: ${{ parameters.buildPlatforms }}
+      # This build is already matrix'd on configuration, so
+      # just pass a single config into the download template.
+      buildConfigurations:
+        - $(BuildConfiguration)
+      artifactStem: ${{ parameters.artifactStem }}
+
+  - template: steps-ensure-nuget-version.yml
+
+# In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
+# This should be `task: NuGetCommand@2`
+  - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
+    displayName: NuGet pack
+    inputs:
+      command: pack
+      packagesToPack: $(Build.SourcesDirectory)\src\winconpty\package\winconpty.nuspec
+      packDestination: '$(Build.ArtifactStagingDirectory)/nupkg'
+      versioningScheme: byEnvVar
+      versionEnvVar: XES_PACKAGEVERSIONNUMBER
+
+  - ${{ if eq(parameters.codeSign, true) }}:
+    - task: EsrpCodeSigning@1
+      displayName: Submit *.nupkg to ESRP for code signing
+      inputs:
+        ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
+        FolderPath: $(Build.ArtifactStagingDirectory)/nupkg
+        Pattern: '*.nupkg'
+        UseMinimatch: true
+        signConfigType: inlineSignParams
+        inlineOperation: >-
+          [
+              {
+                  "KeyCode": "CP-401405",
+                  "OperationCode": "NuGetSign",
+                  "Parameters": {},
+                  "ToolName": "sign",
+                  "ToolVersion": "1.0"
+              },
+              {
+                  "KeyCode": "CP-401405",
+                  "OperationCode": "NuGetVerify",
+                  "Parameters": {},
+                  "ToolName": "sign",
+                  "ToolVersion": "1.0"
+              }
+          ]
+
+  - ${{ if eq(parameters.generateSbom, true) }}:
+    - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
+      displayName: 'Generate SBOM manifest (conpty)'
+      inputs:
+       BuildDropPath: '$(System.ArtifactsDirectory)/nupkg'
+       BuildComponentPath: '$(Build.SourcesDirectory)/bin'
+
+    - task: DropValidatorTask@0
+      displayName: 'Validate conpty SBOM manifest'
+      inputs:
+        BuildDropPath: '$(System.ArtifactsDirectory)/nupkg'
+        OutputPath: 'output.json'
+        ValidateSignature: true
+        Verbosity: 'Verbose'
+
+  - publish: $(Build.ArtifactStagingDirectory)\nupkg
+    artifact: conpty-nupkg-$(BuildConfiguration)${{ parameters.artifactStem }}
+    displayName: Publish nupkg
diff --git a/build/pipelines/templates/pgo-build-and-publish-nuget-job.yml b/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml
similarity index 57%
rename from build/pipelines/templates/pgo-build-and-publish-nuget-job.yml
rename to build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml
index 2c36f7cf158..b61a22c4fc1 100644
--- a/build/pipelines/templates/pgo-build-and-publish-nuget-job.yml
+++ b/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml
@@ -1,14 +1,26 @@
-# From our friends at MUX: https://github.com/microsoft/microsoft-ui-xaml/blob/main/build/AzurePipelinesTemplates/MUX-BuildAndPublishPGONuGet-Job.yml
-
 parameters:
-  dependsOn: ''
-  pgoArtifact: PGO
+  - name: buildConfiguration
+    type: string
+  - name: pool
+    type: object
+    default: []
+  - name: dependsOn
+    type: object
+    default: null
+  - name: artifactStem
+    type: string
+    default: ''
+  - name: jobName
+    type: string
+    default: BuildAndPublishPGONuget
 
 jobs:
-- job: BuildAndPublishPGONuGet
+- job: ${{ parameters.jobName }}
+  ${{ if ne(length(parameters.pool), 0) }}:
+    pool: ${{ parameters.pool }}
   dependsOn: ${{ parameters.dependsOn }}
-  pool:
-    vmImage: 'windows-2019'
+  displayName: Package and Publish PGO Databases
+
   variables:
     artifactsPath: $(Build.SourcesDirectory)\Artifacts
     pgoToolsPath: $(Build.SourcesDirectory)\build\PGO
@@ -16,20 +28,25 @@ jobs:
     nuspecFilename: PGO.nuspec
 
   steps:
-  - task: DownloadBuildArtifacts@0
+  - checkout: self
+    clean: true
+    # It is important that this be 0, otherwise git will not fetch the branch ref names that the PGO rules require.
+    fetchDepth: 0
+    submodules: false
+    persistCredentials: false
+
+  - task: DownloadPipelineArtifact@2
+    displayName: Download Final PGO Databases
     inputs:
-      artifactName: ${{ parameters.pgoArtifact }}
+      artifact: pgd-merged-${{ parameters.buildConfiguration }}${{ parameters.artifactStem }}
       downloadPath: $(artifactsPath)
 
+  - template: steps-ensure-nuget-version.yml
+
   - task: NuGetAuthenticate@0
     inputs:
       nuGetServiceConnections: 'Terminal Public Artifact Feed'
 
-  - task: NuGetToolInstaller@0
-    displayName: 'Use NuGet 5.8.0'
-    inputs:
-      versionSpec: 5.8.0
-
   # In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
   # This should be `task: NuGetCommand@2`
   - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
@@ -45,12 +62,11 @@ jobs:
     displayName: 'Create PGO Nuget'
     inputs:
       solution: $(pgoToolsPath)\PGO.DB.proj
-      msbuildArguments: '/t:CreatePGONuGet /p:PGOBuildMode=Instrument /p:PGDPathForAllArch=$(artifactsPath)\${{ parameters.pgoArtifact }} /p:PGOOutputPath=$(Build.ArtifactStagingDirectory)'
+      msbuildArguments: '/t:CreatePGONuGet /p:PGOBuildMode=Instrument /p:PGDPathForAllArch=$(artifactsPath) /p:PGOOutputPath=$(Build.ArtifactStagingDirectory)'
 
-  - task: PublishBuildArtifacts@1
-    inputs:
-      pathToPublish: $(Build.ArtifactStagingDirectory)
-      artifactName: ${{ parameters.pgoArtifact }}
+  - publish: $(Build.ArtifactStagingDirectory)
+    artifact: pgo-nupkg-${{ parameters.buildConfiguration }}${{ parameters.artifactStem }}
+    displayName: "Publish Pipeline Artifact"
 
   - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
     displayName: 'NuGet push'
diff --git a/build/pipelines/templates-v2/job-pgo-merge-pgd.yml b/build/pipelines/templates-v2/job-pgo-merge-pgd.yml
new file mode 100644
index 00000000000..8b341b463eb
--- /dev/null
+++ b/build/pipelines/templates-v2/job-pgo-merge-pgd.yml
@@ -0,0 +1,75 @@
+parameters:
+  - name: buildConfiguration
+    type: string
+  - name: buildPlatforms
+    type: object
+  - name: pool
+    type: object
+    default: []
+  - name: dependsOn
+    type: object
+    default: null
+  - name: artifactStem
+    type: string
+    default: ''
+  - name: jobName
+    type: string
+    default: MergePGD
+
+jobs:
+- job: ${{ parameters.jobName }}
+  ${{ if ne(length(parameters.pool), 0) }}:
+    pool: ${{ parameters.pool }}
+  dependsOn: ${{ parameters.dependsOn }}
+  displayName: Merge PGO Counts for ${{ parameters.buildConfiguration }}
+
+  steps:
+  # The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves.
+  - script: |
+      "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt
+      set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt
+      del %TEMP%\vsinstalldir.txt
+      call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat"
+      echo VCToolsInstallDir = %VCToolsInstallDir%
+      echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
+    displayName: 'Retrieve VC tools directory'
+
+  - ${{ each platform in parameters.buildPlatforms }}:
+    - task: DownloadPipelineArtifact@2
+      displayName: Download PGO Databases for ${{ platform }}
+      inputs:
+        artifactName: build-${{ platform }}-${{ parameters.buildConfiguration }}${{ parameters.artifactStem }}
+        itemPattern: '**/*.pgd'
+        downloadPath: '$(Build.SourcesDirectory)/pgd/${{ platform }}/${{ parameters.buildConfiguration }}'
+    - task: DownloadPipelineArtifact@2
+      displayName: Download PGO Counts for ${{ platform }}
+      inputs:
+        artifactName: pgc-intermediates-${{ platform }}-${{ parameters.buildConfiguration }}${{ parameters.artifactStem }}
+        downloadPath: '$(Build.SourcesDirectory)/pgc/${{ platform }}/${{ parameters.buildConfiguration }}'
+    - pwsh: |-
+        $Arch = '${{ platform }}'
+        $Conf = '${{ parameters.buildConfiguration }}'
+        $PGCDir = '$(Build.SourcesDirectory)/pgc/${{ platform }}/${{ parameters.buildConfiguration }}'
+        $PGDDir = '$(Build.SourcesDirectory)/pgd/${{ platform }}/${{ parameters.buildConfiguration }}'
+        # Flatten the PGD directory
+        Get-ChildItem $PGDDir -Recurse -Filter *.pgd | Move-Item -Destination $PGDDir -Verbose
+        Get-ChildItem $PGCDir -Filter *.pgc |
+          ForEach-Object {
+            $Parts = $_.Name -Split "!";
+            $_ | Add-Member Module $Parts[0] -PassThru
+          } |
+          Group-Object Module |
+          ForEach-Object {
+            & "$(VCToolsInstallDir)\bin\Hostx64\${{ platform }}\pgomgr.exe" /merge $_.Group.FullName "$PGDDir\$($_.Name).pgd"
+          }
+      displayName: Merge PGO Counts for ${{ platform }}
+    - task: CopyFiles@2
+      displayName: 'Copy merged pgds to artifact staging'
+      inputs:
+        sourceFolder: '$(Build.SourcesDirectory)/pgd/${{ platform }}/${{ parameters.buildConfiguration }}'
+        contents: '**\*.pgd'
+        targetFolder: '$(Build.ArtifactStagingDirectory)\out-pgd\${{ platform }}'
+
+  - publish: $(Build.ArtifactStagingDirectory)\out-pgd
+    artifact: pgd-merged-${{ parameters.buildConfiguration }}${{ parameters.artifactStem }}
+    displayName: "Publish merged PGDs"
diff --git a/build/pipelines/templates-v2/job-publish-symbols.yml b/build/pipelines/templates-v2/job-publish-symbols.yml
new file mode 100644
index 00000000000..6c37f0f4cbd
--- /dev/null
+++ b/build/pipelines/templates-v2/job-publish-symbols.yml
@@ -0,0 +1,81 @@
+parameters:
+  - name: includePublicSymbolServer
+    type: boolean
+    default: false
+  - name: pool
+    type: object
+    default: []
+  - name: dependsOn
+    type: object
+    default: null
+  - name: artifactStem
+    type: string
+    default: ''
+  - name: jobName
+    type: string
+    default: PublishSymbols
+
+jobs:
+- job: ${{ parameters.jobName }}
+  ${{ if ne(length(parameters.pool), 0) }}:
+    pool: ${{ parameters.pool }}
+  ${{ if eq(parameters.includePublicSymbolServer, true) }}:
+    displayName: Publish Symbols to Internal and MSDL
+  ${{ else }}:
+    displayName: Publish Symbols Internally
+  dependsOn: ${{ parameters.dependsOn }}
+  steps:
+  - checkout: self
+    clean: true
+    fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
+    submodules: true
+    persistCredentials: True
+
+  - task: PkgESSetupBuild@12
+    displayName: Package ES - Setup Build
+    inputs:
+      disableOutputRedirect: true
+
+  - task: DownloadPipelineArtifact@2
+    displayName: Download all PDBs from all prior build phases
+    inputs:
+      itemPattern: '**/*.pdb'
+      targetPath: '$(Build.SourcesDirectory)/bin'
+
+  - task: PublishSymbols@2
+    displayName: Publish Symbols (to current Azure DevOps tenant)
+    continueOnError: True
+    inputs:
+      SymbolsFolder: '$(Build.SourcesDirectory)/bin'
+      SearchPattern: '**/*.pdb'
+      IndexSources: false
+      DetailedLog: true
+      SymbolsMaximumWaitTime: 30
+      SymbolServerType: 'TeamServices'
+      SymbolsProduct: 'Windows Terminal Converged Symbols'
+      SymbolsVersion: '$(XES_APPXMANIFESTVERSION)'
+    env:
+      LIB: $(Build.SourcesDirectory)
+
+  - ${{ if eq(parameters.includePublicSymbolServer, true) }}:
+    - task: PublishSymbols@2
+      displayName: 'Publish symbols to MSDL'
+      continueOnError: True
+      inputs:
+        SymbolsFolder: '$(Build.SourcesDirectory)/bin'
+        SearchPattern: '**/*.pdb'
+        IndexSources: false
+        DetailedLog: true
+        SymbolsMaximumWaitTime: 30
+        SymbolServerType: 'TeamServices'
+        SymbolsProduct: 'Windows Terminal Converged Symbols'
+        SymbolsVersion: '$(XES_APPXMANIFESTVERSION)'
+        # The ADO task does not support indexing of GitHub sources.
+      # There is a bug which causes this task to fail if LIB includes an inaccessible path (even though it does not depend on it).
+      # To work around this issue, we just force LIB to be any dir that we know exists.
+      # Copied from https://github.com/microsoft/icu/blob/f869c214adc87415dfe751d81f42f1bca55dcf5f/build/azure-nuget.yml#L564-L583
+      env:
+        LIB: $(Build.SourcesDirectory)
+        ArtifactServices_Symbol_AccountName: microsoftpublicsymbols
+        ArtifactServices_Symbol_PAT: $(ADO_microsoftpublicsymbols_PAT)
diff --git a/build/pipelines/templates-v2/job-submit-windows-vpack.yml b/build/pipelines/templates-v2/job-submit-windows-vpack.yml
new file mode 100644
index 00000000000..2bed2899cf0
--- /dev/null
+++ b/build/pipelines/templates-v2/job-submit-windows-vpack.yml
@@ -0,0 +1,70 @@
+parameters:
+  - name: buildConfiguration
+    type: string
+  - name: generateSbom
+    type: boolean
+    default: false
+  - name: pool
+    type: object
+    default: []
+  - name: dependsOn
+    type: object
+    default: null
+  - name: artifactStem
+    type: string
+    default: ''
+
+jobs:
+- job: VPack
+  ${{ if ne(length(parameters.pool), 0) }}:
+    pool: ${{ parameters.pool }}
+  displayName: Create and Submit Windows vPack
+  dependsOn: ${{ parameters.dependsOn }}
+  steps:
+  - checkout: self
+    clean: true
+    fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
+    submodules: true
+    persistCredentials: True
+
+  - task: PkgESSetupBuild@12
+    displayName: Package ES - Setup Build
+    inputs:
+      disableOutputRedirect: true
+
+  - task: DownloadPipelineArtifact@2
+    displayName: Download MSIX Bundle Artifact
+    inputs:
+      artifactName: appxbundle-${{ parameters.buildConfiguration }}${{ parameters.artifactStem }}
+      downloadPath: '$(Build.SourcesDirectory)/bundle'
+
+  # Rename to known/fixed name for Windows build system
+  - powershell: |-
+      # Create vpack directory and place item inside
+      $TargetFolder = New-Item -Type Directory '$(Build.SourcesDirectory)/WindowsTerminal.app'
+      Get-ChildItem bundle/Microsoft.WindowsTerminal_*.msixbundle | Move-Item (Join-Path $TargetFolder.FullName 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle') -Verbose
+    displayName: Stage packages for vpack
+
+  - task: PkgESVPack@12
+    displayName: 'Package ES - VPack'
+    env:
+      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+    inputs:
+      sourceDirectory: '$(Build.SourcesDirectory)/WindowsTerminal.app'
+      description: VPack for the Windows Terminal Application
+      pushPkgName: WindowsTerminal.app
+      owner: conhost
+      githubToken: $(GitHubTokenForVpackProvenance)
+
+  - publish: $(XES_VPACKMANIFESTDIRECTORY)
+    artifact: vpack-manifest${{ parameters.artifactStem }}
+    displayName: 'Publish VPack Manifest to Drop'
+
+  - task: PkgESFCIBGit@12
+    displayName: 'Submit VPack Manifest to Windows'
+    inputs:
+      configPath: '$(Build.SourcesDirectory)\build\config\GitCheckin.json'
+      artifactsDirectory: $(XES_VPACKMANIFESTDIRECTORY)
+      prTimeOut: 5
+
diff --git a/build/pipelines/templates-v2/steps-create-signing-config.yml b/build/pipelines/templates-v2/steps-create-signing-config.yml
new file mode 100644
index 00000000000..790d7a3fa86
--- /dev/null
+++ b/build/pipelines/templates-v2/steps-create-signing-config.yml
@@ -0,0 +1,35 @@
+parameters:
+  - name: stage
+    type: string
+  - name: outFile
+    type: string
+  - name: fragments
+    type: string
+
+# This build step template takes all files named "esrp.STAGE.batch.*.json"
+# and merges them into a single output signing config.
+#
+# We generate the batch signing config by sticking together multiple "batches".
+# The filter below (with Fragments) works by splitting the filename, esrp.s.batch.x.json,
+# to get 'x' and then checking whether x is in Fragments.
+# We have to manually strip comments out of the batch fragments due to https://github.com/PowerShell/PowerShell/issues/14553
+
+steps:
+  - pwsh: |-
+      $SignBatchFiles = (Get-Item build/config/esrp.${{ parameters.stage }}.batch.*.json)
+      $Fragments = "${{ parameters.fragments }}"
+      If (-Not [String]::IsNullOrWhiteSpace($Fragments)) {
+        $FragmentList = $Fragments -Split ";"
+        If ($FragmentList.Length -Gt 0) {
+          $SignBatchFiles = $SignBatchFiles | Where-Object { ($_.Name -Split '\.')[3] -In $FragmentList }
+        }
+      }
+      Write-Host "Found $(@($SignBatchFiles).Length) Signing Configs"
+      Write-Host ($SignBatchFiles.Name -Join ";")
+      $FinalSignConfig = @{
+        Version = "1.0.0";
+        UseMinimatch = $false;
+        SignBatches = @($SignBatchFiles | ForEach-Object { Get-Content $_ | Where-Object { $_ -NotMatch "^\s*\/\/" } | ConvertFrom-Json -Depth 10 });
+      }
+      $FinalSignConfig | ConvertTo-Json -Depth 10 | Out-File -Encoding utf8 "${{ parameters.outFile }}"
+    displayName: Merge ${{ parameters.stage }} signing configs (${{ parameters.outFile }})
diff --git a/build/pipelines/templates-v2/steps-download-bin-dir-artifact.yml b/build/pipelines/templates-v2/steps-download-bin-dir-artifact.yml
new file mode 100644
index 00000000000..e5e90cc8ea2
--- /dev/null
+++ b/build/pipelines/templates-v2/steps-download-bin-dir-artifact.yml
@@ -0,0 +1,24 @@
+parameters:
+  - name: buildConfigurations
+    type: object
+  - name: buildPlatforms
+    type: object
+  - name: artifactStem
+    type: string
+    default: ''
+
+steps:
+- ${{ each configuration in parameters.buildConfigurations }}:
+  - ${{ each platform in parameters.buildPlatforms }}:
+    - task: DownloadPipelineArtifact@2
+      displayName: Download artifacts for ${{ platform }} ${{ configuration }}
+      inputs:
+        # Make sure to download the entire artifact, because it includes the SPDX SBOM
+        artifactName: build-${{ platform }}-${{ configuration }}${{ parameters.artifactStem }}
+        # Downloading to the source directory should ensure that the later SBOM generator can see the earlier SBOMs.
+        ${{ if eq(platform, 'x86') }}:
+          downloadPath: '$(Build.SourcesDirectory)/bin/Win32/${{ configuration }}'
+        ${{ elseif eq(platform, 'Any CPU') }}:
+          downloadPath: '$(Build.SourcesDirectory)/bin/AnyCPU/${{ configuration }}'
+        ${{ else }}:
+          downloadPath: '$(Build.SourcesDirectory)/bin/${{ platform }}/${{ configuration }}'
diff --git a/build/pipelines/templates-v2/steps-ensure-nuget-version.yml b/build/pipelines/templates-v2/steps-ensure-nuget-version.yml
new file mode 100644
index 00000000000..fb5cd75e4c8
--- /dev/null
+++ b/build/pipelines/templates-v2/steps-ensure-nuget-version.yml
@@ -0,0 +1,5 @@
+steps:
+- task: NuGetToolInstaller@1
+  displayName: Use NuGet 6.6.1
+  inputs:
+    versionSpec: 6.6.1
diff --git a/build/pipelines/templates-v2/steps-fetch-and-prepare-localizations.yml b/build/pipelines/templates-v2/steps-fetch-and-prepare-localizations.yml
new file mode 100644
index 00000000000..8ace357aad3
--- /dev/null
+++ b/build/pipelines/templates-v2/steps-fetch-and-prepare-localizations.yml
@@ -0,0 +1,27 @@
+parameters:
+  - name: includePseudoLoc
+    type: boolean
+    default: true
+
+steps:
+  - task: TouchdownBuildTask@1
+    displayName: Download Localization Files
+    inputs:
+      teamId: 7105
+      authId: $(TouchdownAppId)
+      authKey: $(TouchdownAppKey)
+      resourceFilePath: |
+        src\cascadia\**\en-US\*.resw
+      appendRelativeDir: true
+      localizationTarget: false
+      ${{ if eq(parameters.includePseudoLoc, true) }}:
+        pseudoSetting: Included
+
+  - pwsh: |-
+      $Files = Get-ChildItem . -R -Filter 'Resources.resw' | ? FullName -Like '*en-US\*\Resources.resw'
+      $Files | % { Move-Item -Verbose $_.Directory $_.Directory.Parent.Parent -EA:Ignore }
+    displayName: Move Loc files into final locations
+
+  - pwsh: |-
+      ./build/scripts/Copy-ContextMenuResourcesToCascadiaPackage.ps1
+    displayName: Copy the Context Menu Loc Resources to CascadiaPackage
diff --git a/build/pipelines/templates/restore-nuget-steps.yml b/build/pipelines/templates-v2/steps-restore-nuget.yml
similarity index 88%
rename from build/pipelines/templates/restore-nuget-steps.yml
rename to build/pipelines/templates-v2/steps-restore-nuget.yml
index aabae84504c..bd0c067531c 100644
--- a/build/pipelines/templates/restore-nuget-steps.yml
+++ b/build/pipelines/templates-v2/steps-restore-nuget.yml
@@ -1,14 +1,12 @@
 steps:
-- task: NuGetToolInstaller@0
-  displayName: 'Use NuGet 6.3.0'
-  inputs:
-    versionSpec: 6.3.0
+- template: steps-ensure-nuget-version.yml
 
 - task: NuGetAuthenticate@0
 
 - script: |-
     echo ##vso[task.setvariable variable=NUGET_RESTORE_MSBUILD_ARGS]/p:Platform=$(BuildPlatform)
   displayName: Ensure NuGet restores for $(BuildPlatform)
+  condition: and(succeeded(), ne(variables['BuildPlatform'], 'Any CPU'))
 
 # In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
 # This should be `task: NuGetCommand@2`
diff --git a/build/pipelines/templates/build-console-audit-job.yml b/build/pipelines/templates/build-console-audit-job.yml
deleted file mode 100644
index 69b0b05ce31..00000000000
--- a/build/pipelines/templates/build-console-audit-job.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-parameters:
-  platform: ''
-  additionalBuildArguments: ''
-
-jobs:
-- job: Build${{ parameters.platform }}AuditMode
-  displayName: Static Analysis Build ${{ parameters.platform }}
-  variables:
-    BuildConfiguration: AuditMode
-    BuildPlatform: ${{ parameters.platform }}
-  pool:
-    ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
-      name: SHINE-OSS-L
-    ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
-      name: SHINE-INT-L
-    demands: ImageOverride -equals SHINE-VS17-Latest
-
-  steps:
-  - checkout: self
-    submodules: true
-    clean: true
-    fetchDepth: 1
-
-  - template: restore-nuget-steps.yml
-
-  - task: VSBuild@1
-    displayName: 'Build solution **\OpenConsole.sln'
-    inputs:
-      solution: '**\OpenConsole.sln'
-      platform: '$(BuildPlatform)'
-      configuration: '$(BuildConfiguration)'
-      msbuildArgs: ${{ parameters.additionalBuildArguments }}
-      clean: true
-      maximumCpuCount: true
diff --git a/build/pipelines/templates/build-console-ci.yml b/build/pipelines/templates/build-console-ci.yml
deleted file mode 100644
index 48c27052e29..00000000000
--- a/build/pipelines/templates/build-console-ci.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-parameters:
-  configuration: 'Release'
-  branding: 'Dev'
-  platform: ''
-  additionalBuildArguments: ''
-
-jobs:
-- job: Build${{ parameters.platform }}${{ parameters.configuration }}${{ parameters.branding }}
-  displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }} ${{ parameters.branding }}
-  variables:
-    BuildConfiguration: ${{ parameters.configuration }}
-    BuildPlatform: ${{ parameters.platform }}
-    WindowsTerminalBranding: ${{ parameters.branding }}
-    EnableRichCodeNavigation: true
-  pool:
-    ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
-      name: SHINE-OSS-L
-    ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
-      name: SHINE-INT-L
-    demands: ImageOverride -equals SHINE-VS17-Latest
-
-  steps:
-  - template: build-console-steps.yml
-    parameters:
-      additionalBuildArguments: ${{ parameters.additionalBuildArguments }}
-
-  # It appears that the Component Governance build task that gets automatically injected stopped working
-  # when we renamed our main branch.
-  - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
-    displayName: 'Component Detection'
-    condition: and(succeededOrFailed(), not(eq(variables['Build.Reason'], 'PullRequest')))
diff --git a/build/pipelines/templates/build-console-compliance-job.yml b/build/pipelines/templates/build-console-compliance-job.yml
deleted file mode 100644
index 0f885cfcce7..00000000000
--- a/build/pipelines/templates/build-console-compliance-job.yml
+++ /dev/null
@@ -1,203 +0,0 @@
-jobs:
-- job: Compliance
-  # We don't *need* a matrix but there's no other way to set parameters on a "job"
-  # in the AzDO YAML syntax. It would have to be a "stage" or a "template".
-  # Doesn't matter. We're going to do compliance on Release x64 because
-  # that's the one all the tooling works against for sure.
-  strategy:
-    matrix:
-      Release_x64:
-        BuildConfiguration: Release
-        BuildPlatform: x64
-  displayName: Validate Security and Compliance
-  timeoutInMinutes: 240
-  steps:
-  - checkout: self
-    clean: true
-    submodules: true
-    persistCredentials: True
-  - task: PkgESSetupBuild@12
-    displayName: Package ES - Setup Build
-    inputs:
-      disableOutputRedirect: true
-  - task: PowerShell@2
-    displayName: Rationalize Build Platform
-    inputs:
-      targetType: inline
-      script: >-
-        $Arch = "$(BuildPlatform)"
-
-        If ($Arch -Eq "x86") { $Arch = "Win32" }
-
-        Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
-  - template: restore-nuget-steps.yml
-  - task: UniversalPackages@0
-    displayName: Download terminal-internal Universal Package
-    inputs:
-      feedListDownload: 2b3f8893-a6e8-411f-b197-a9e05576da48
-      packageListDownload: e82d490c-af86-4733-9dc4-07b772033204
-      versionListDownload: $(TerminalInternalPackageVersion)
-  - task: TouchdownBuildTask@1
-    displayName: Download Localization Files
-    inputs:
-      teamId: 7105
-      authId: $(TouchdownAppId)
-      authKey: $(TouchdownAppKey)
-      resourceFilePath: >-
-        src\cascadia\TerminalApp\Resources\en-US\Resources.resw
-
-        src\cascadia\TerminalControl\Resources\en-US\Resources.resw
-
-        src\cascadia\TerminalConnection\Resources\en-US\Resources.resw
-
-        src\cascadia\TerminalSettingsModel\Resources\en-US\Resources.resw
-
-        src\cascadia\TerminalSettingsEditor\Resources\en-US\Resources.resw
-
-        src\cascadia\CascadiaPackage\Resources\en-US\Resources.resw
-      appendRelativeDir: true
-      localizationTarget: false
-      pseudoSetting: Included
-  - task: PowerShell@2
-    displayName: Move Loc files one level up
-    inputs:
-      targetType: inline
-      script: >-
-        $Files = Get-ChildItem . -R -Filter 'Resources.resw' | ? FullName -Like '*en-US\*\Resources.resw'
-
-        $Files | % { Move-Item -Verbose $_.Directory $_.Directory.Parent.Parent -EA:Ignore }
-      pwsh: true
-
-  # 1ES Component Governance onboarding (Detects open source components). See https://docs.opensource.microsoft.com/tools/cg.html
-  - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
-    displayName: Component Detection
-
-  # # PREfast and PoliCheck need Node. Install that first.
-  - task: NodeTool@0
-
-  # !!! NOTE !!! Run PREfast first. Some of the other tasks are going to run on a completed build.
-  # PREfast is going to build the code as a part of its analysis and the generated sources
-  # and output binaries will be sufficient for the rest of the analysis.
-  # If you disable this, the other tasks won't likely work. You would have to add a build
-  # step instead that builds the code normally before calling them.
-  # Also... PREfast will rebuild anyway so that's why we're not running a normal build first.
-  # Waste of time to build twice.
-  # PREfast. See https://www.1eswiki.com/wiki/SDL_Native_Rules_Build_Task
-
-  # The following 1ES tasks all operate completely differently and have a different syntax for usage.
-  # Most notable is every one of them has a different way of excluding things.
-  # Go see their 1eswiki.com pages to figure out how to exclude things.
-  # When writing exclusions, try to make them narrow so when new projects/binaries are added, they
-  # cause an error here and have to be explicitly pulled out. Don't write an exclusion so broad
-  # that it will catch other new stuff.
-
-  # https://www.1eswiki.com/wiki/PREfast_Build_Task
-  # Builds the project with C/C++ static analysis tools to find coding flaws and vulnerabilities
-  # !!! WARNING !!! It doesn't work with WAPPROJ packaging projects. Build the sub-projects instead.
-  - task: securedevelopmentteam.vss-secure-development-tools.build-task-prefast.SDLNativeRules@3
-    displayName: 'Run the PREfast SDL Native Rules for MSBuild'
-    condition: succeededOrFailed()
-    inputs:
-      setupCommandlines: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsMSBuildCmd.bat"'
-      msBuildCommandline: msbuild.exe /nologo /m /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /p:WindowsTerminalReleaseBuild=true /p:platform=$(BuildPlatform) /p:configuration=$(BuildConfiguration) /t:Terminal\Window\WindowsTerminal /p:VisualStudioVersion=17.0 $(Build.SourcesDirectory)\OpenConsole.sln
-      msBuildVersion: "17.0"
-
-  # Copies output from PREfast SDL Native Rules task to expected location for consumption by PkgESSecComp
-  - task: CopyFiles@1
-    displayName: 'Copy PREfast xml files to SDLNativeRulesDir'
-    inputs:
-      SourceFolder: '$(Agent.BuildDirectory)'
-      Contents: |
-        **\*.nativecodeanalysis.xml
-      TargetFolder: '$(Agent.BuildDirectory)\_sdt\logs\SDLNativeRules'
-
-  # https://www.1eswiki.com/index.php?title=PoliCheck_Build_Task
-  # Scans the text of source code, comments, and content for terminology that could be sensitive for legal, cultural, or geopolitical reasons.
-  # (Also finds vulgarities... takes all the fun out of everything.)
-  - task: securedevelopmentteam.vss-secure-development-tools.build-task-policheck.PoliCheck@2
-    displayName: 'Run PoliCheck'
-    inputs:
-      targetType: F
-      targetArgument: $(Build.SourcesDirectory)
-      result: PoliCheck.xml
-      optionsFC: 1
-      optionsXS: 1
-      optionsUEPath: $(Build.SourcesDirectory)\build\config\PolicheckExclusions.xml
-      optionsHMENABLE: 0
-    continueOnError: true
-
-  # https://www.1eswiki.com/wiki/CredScan_Azure_DevOps_Build_Task
-  # Searches through source code and build outputs for a credential left behind in the open
-  - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3
-    displayName: 'Run CredScan'
-    inputs:
-      outputFormat: pre
-      # suppressionsFile: LocalSuppressions.json
-      batchSize: 20
-      debugMode: false
-    continueOnError: true
-
-  # https://www.1eswiki.com/wiki/BinSkim_Build_Task
-  # Searches managed and unmanaged binaries for known security vulnerabilities.
-  - task: securedevelopmentteam.vss-secure-development-tools.build-task-binskim.BinSkim@4
-    displayName: 'Run BinSkim'
-    inputs:
-      TargetPattern: guardianGlob
-      # See https://aka.ms/gdn-globs for how to do match patterns
-      AnalyzeTargetGlob: $(Build.SourcesDirectory)\bin\**\*.dll;$(Build.SourcesDirectory)\bin\**\*.exe;-:file|**\Microsoft.UI.Xaml.dll;-:file|**\Microsoft.Toolkit.Win32.UI.XamlHost.dll;-:file|**\vcruntime*.dll;-:file|**\vcomp*.dll;-:file|**\vccorlib*.dll;-:file|**\vcamp*.dll;-:file|**\msvcp*.dll;-:file|**\concrt*.dll;-:file|**\TerminalThemeHelpers*.dll
-    continueOnError: true
-
-  # Set XES_SERIALPOSTBUILDREADY to run Security and Compliance task once per build
-  - powershell: Write-Host "##vso[task.setvariable variable=XES_SERIALPOSTBUILDREADY;]true"
-    displayName: 'Set XES_SERIALPOSTBUILDREADY Vars'
-
-  # https://www.osgwiki.com/wiki/Package_ES_Security_and_Compliance
-  # Does a few things:
-  # - Ensures that Windows-required compliance tasks are run either inside this task
-  #   or were run as a previous step prior to this one
-  #   (PREfast, PoliCheck, Credscan)
-  # - Runs Windows-specific compliance tasks inside the task
-  #   + CheckCFlags - ensures that compiler and linker flags meet Windows standards
-  #   + CFGCheck/XFGCheck - ensures that Control Flow Guard (CFG) or
-  #                         eXtended Flow Guard (XFG) are enabled on binaries
-  #                         NOTE: CFG is deprecated and XFG isn't fully ready yet.
-  #                         NOTE2: CFG fails on an XFG'd binary
-  # - Brokers all security/compliance task logs to "Trust Services Automation (TSA)" (https://aka.ms/tsa)
-  #   which is a system that maps all errors into the appropriate bug database
-  #   template for each organization since they all vary. It should also suppress
-  #   new bugs when one already exists for the product.
-  #   This one is set up to go to the OS repository and use the given parameters
-  #   to file bugs to our AzDO product path.
-  #   If we don't use PkgESSecComp to do this for us, we need to install the TSA task
-  #   ourselves in this pipeline to finalize data upload and bug creation.
-  # !!! NOTE !!! This task goes *LAST* after any other compliance tasks so it catches their logs
-  - task: PkgESSecComp@10
-    displayName: 'Security and Compliance tasks'
-    inputs:
-      fileNewBugs: false
-      areaPath: 'OS\WDX\DXP\WinDev\Terminal'
-      teamProject: 'OS'
-      iterationPath: 'OS\Future'
-      bugTags: 'TerminalReleaseCompliance'
-      scanAll: true
-      errOnBugs: false
-      failOnStdErr: true
-      taskLogVerbosity: Diagnostic
-      secCompConfigFromTask: |
-        # Overrides default build sources directory
-        sourceTargetOverrideAll: $(Build.SourcesDirectory)
-        # Overrides default build binaries directory when "Scan all" option is specified
-        binariesTargetOverrideAll: $(Build.SourcesDirectory)\bin
-
-        # Set the tools to false if they should not run in the build
-        tools:
-            - toolName: CheckCFlags
-              enable: true
-            - toolName: CFGCheck
-              enable: true
-            - toolName: Policheck
-              enable: false
-            - toolName: CredScan
-              enable: false
-            - toolName: XFGCheck
-              enable: false
diff --git a/build/pipelines/templates/build-console-fuzzing.yml b/build/pipelines/templates/build-console-fuzzing.yml
deleted file mode 100644
index 8277ab9dc3a..00000000000
--- a/build/pipelines/templates/build-console-fuzzing.yml
+++ /dev/null
@@ -1,90 +0,0 @@
-parameters:
-  configuration: 'Fuzzing'
-  platform: ''
-  additionalBuildArguments: ''
-
-jobs:
-- job: Build${{ parameters.platform }}${{ parameters.configuration }}
-  displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
-  variables:
-    BuildConfiguration: ${{ parameters.configuration }}
-    BuildPlatform: ${{ parameters.platform }}
-  pool:
-    ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
-      name: SHINE-OSS-L
-    ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
-      name: SHINE-INT-L
-    demands: ImageOverride -equals SHINE-VS17-Latest
-
-  steps:
-  - checkout: self
-    submodules: true
-    clean: true
-
-  - template: restore-nuget-steps.yml
-
-  # The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves.
-  - script: |
-      "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt
-      set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt
-      del %TEMP%\vsinstalldir.txt
-      call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat"
-      echo VCToolsInstallDir = %VCToolsInstallDir%
-      echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
-    displayName: 'Retrieve VC tools directory'
-
-  - task: VSBuild@1
-    displayName: 'Build solution **\OpenConsole.sln'
-    inputs:
-      solution: '**\OpenConsole.sln'
-      platform: '$(BuildPlatform)'
-      configuration: '$(BuildConfiguration)'
-      msbuildArgs: "${{ parameters.additionalBuildArguments }}"
-      clean: true
-      maximumCpuCount: true
-
-  - task: PowerShell@2
-    displayName: 'Rationalize build platform'
-    inputs:
-      targetType: inline
-      script: |
-        $Arch = "$(BuildPlatform)"
-        If ($Arch -Eq "x86") { $Arch = "Win32" }
-        Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
-
-  - task: CopyFiles@2
-    displayName: 'Copy result logs to Artifacts'
-    inputs:
-      Contents: |
-       **/*.wtl
-       **/*onBuildMachineResults.xml
-       ${{ parameters.testLogPath }}
-      TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/test'
-      OverWrite: true
-      flattenFolders: true
-
-  - task: CopyFiles@2
-    displayName: 'Copy outputs needed for test runs to Artifacts'
-    inputs:
-      Contents: |
-       $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.exe
-       $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.dll
-       $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.xml
-       **/Microsoft.VCLibs.*.appx
-       **/TestHostApp/*.exe
-       **/TestHostApp/*.dll
-       **/TestHostApp/*.xml
-       !**/*.pdb
-       !**/*.ipdb
-       !**/*.obj
-       !**/*.pch
-      TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/test'
-      OverWrite: true
-      flattenFolders: true
-    condition: succeeded()
-
-  - task: PublishBuildArtifacts@1
-    displayName: 'Publish All Build Artifacts'
-    inputs:
-      PathtoPublish: '$(Build.ArtifactStagingDirectory)'
-      ArtifactName: 'fuzzingBuildOutput'
diff --git a/build/pipelines/templates/build-console-pgo.yml b/build/pipelines/templates/build-console-pgo.yml
deleted file mode 100644
index b1b4c501633..00000000000
--- a/build/pipelines/templates/build-console-pgo.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-parameters:
-  configuration: 'Release'
-  platform: ''
-  additionalBuildArguments: ''
-  minimumExpectedTestsExecutedCount: 1  # Sanity check for minimum expected tests to be reported
-  rerunPassesRequiredToAvoidFailure: 5
-
-jobs:
-- job: Build${{ parameters.platform }}${{ parameters.configuration }}
-  displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
-  variables:
-    BuildConfiguration: ${{ parameters.configuration }}
-    BuildPlatform: ${{ parameters.platform }}
-    PGOBuildMode: 'Instrument'
-  pool:
-    ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
-      name: SHINE-OSS-L
-    ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
-      name: SHINE-INT-L
-    demands: ImageOverride -equals SHINE-VS17-Latest
-
-  steps:
-  - template: build-console-steps.yml
-    parameters:
-      additionalBuildArguments: '${{ parameters.additionalBuildArguments }}'
-
-- template: helix-runtests-job.yml
-  parameters:
-    name: 'RunTestsInHelix'
-    dependsOn: Build${{ parameters.platform }}${{ parameters.configuration }}
-    condition: succeeded()
-    testSuite: 'PgoInstrumentationSuite'
-    taefQuery: '@IsPgo=true'
-    configuration: ${{ parameters.configuration }}
-    platform: ${{ parameters.platform }}
-    rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
-
-- template: helix-processtestresults-job.yml
-  parameters:
-    name: 'ProcessTestResults'
-    pgoArtifact: 'PGO'
-    dependsOn:
-    - RunTestsInHelix
-    condition: succeededOrFailed()
-    rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
-    minimumExpectedTestsExecutedCount: ${{ parameters.minimumExpectedTestsExecutedCount }}
-
-- template: pgo-merge-pgd-job.yml
-  parameters:
-    name: 'MergePGD'
-    dependsOn:
-    - ProcessTestResults
-    pgoArtifact: 'PGO'
-    platform: ${{ parameters.platform }}
-    configuration: ${{ parameters.configuration }}
diff --git a/build/pipelines/templates/build-console-steps.yml b/build/pipelines/templates/build-console-steps.yml
deleted file mode 100644
index 947dd353553..00000000000
--- a/build/pipelines/templates/build-console-steps.yml
+++ /dev/null
@@ -1,137 +0,0 @@
-parameters:
-  additionalBuildArguments: ''
-
-steps:
-- checkout: self
-  submodules: true
-  clean: true
-  fetchDepth: 1
-
-- template: restore-nuget-steps.yml
-
-# The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves.
-- script: |
-    "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt
-    set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt
-    del %TEMP%\vsinstalldir.txt
-    call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat"
-    echo VCToolsInstallDir = %VCToolsInstallDir%
-    echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
-  displayName: 'Retrieve VC tools directory'
-
-- task: CmdLine@1
-  displayName: 'Display build machine environment variables'
-  inputs:
-    filename: 'set'
-
-- task: VSBuild@1
-  displayName: 'Build solution **\OpenConsole.sln'
-  inputs:
-    solution: '**\OpenConsole.sln'
-    platform: '$(BuildPlatform)'
-    configuration: '$(BuildConfiguration)'
-    msbuildArgs: "${{ parameters.additionalBuildArguments }} /p:PGOBuildMode=$(PGOBuildMode) /bl:$(Build.SourcesDirectory)\\msbuild.binlog"
-    clean: true
-    maximumCpuCount: true
-
-- task: PowerShell@2
-  displayName: 'Check MSIX for common regressions'
-  # PGO runtime needs its own CRT and it's in the package for convenience.
-  # That will make this script mad so skip since we're not shipping the PGO Instrumentation one anyway.
-  condition: ne(variables['PGOBuildMode'], 'Instrument')
-  inputs:
-    targetType: inline
-    script: |
-      $Package = Get-ChildItem -Recurse -Filter "CascadiaPackage_*.msix"
-      .\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path $Package.FullName
-
-- task: powershell@2
-  displayName: 'Source Index PDBs'
-  condition: ne(variables['PGOBuildMode'], 'Instrument')
-  inputs:
-    targetType: filePath
-    filePath: build\scripts\Index-Pdbs.ps1
-    arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
-    errorActionPreference: silentlyContinue
-
-- task: PowerShell@2
-  displayName: 'Rationalize build platform'
-  inputs:
-    targetType: inline
-    script: |
-      $Arch = "$(BuildPlatform)"
-      If ($Arch -Eq "x86") { $Arch = "Win32" }
-      Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
-
-- task: CopyFiles@2
-  displayName: 'Copy *.msix to Artifacts'
-  inputs:
-    Contents: |
-     **/*.msix
-     **/*.appxsym
-    TargetFolder: '$(Build.ArtifactStagingDirectory)/appx'
-    OverWrite: true
-    flattenFolders: true
-
-- pwsh: |-
-    $TerminalMsixPath = (Get-Item "$(Build.ArtifactStagingDirectory)\appx\Cascadia*.msix").FullName
-    $XamlAppxPath = (Get-Item "src\cascadia\CascadiaPackage\AppPackages\*\Dependencies\$(BuildPlatform)\Microsoft.UI.Xaml*.appx").FullName
-    & .\build\scripts\New-UnpackagedTerminalDistribution.ps1 -TerminalAppX $TerminalMsixPath -XamlAppX $XamlAppxPath -Destination "$(Build.ArtifactStagingDirectory)/unpackaged"
-  displayName: Build Unpackaged Distribution
-
-- publish: $(Build.ArtifactStagingDirectory)/unpackaged
-  artifact: unpackaged-$(BuildPlatform)-$(BuildConfiguration)
-  displayName: Publish Artifact (unpackaged)
-
-- task: CopyFiles@2
-  displayName: 'Copy outputs needed for test runs to Artifacts'
-  inputs:
-    Contents: |
-     $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.exe
-     $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.dll
-     $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.xml
-     **/Microsoft.VCLibs.*.appx
-     **/*unit.test*.dll
-     **/*unit.test*.manifest
-     **/TestHostApp/*.exe
-     **/TestHostApp/*.dll
-     **/TestHostApp/*.xml
-     !**/*.pdb
-     !**/*.ipdb
-     !**/*.obj
-     !**/*.pch
-    TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/test'
-    OverWrite: true
-    flattenFolders: true
-  condition: succeeded()
-
-- task: PublishBuildArtifacts@1
-  displayName: 'Publish All Build Artifacts'
-  inputs:
-    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
-    ArtifactName: 'drop'
-
-- task: CopyFiles@2
-  displayName: 'Copy PGO databases needed for PGO instrumentation run'
-  inputs:
-    Contents: |
-     **/*.pgd
-    TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/PGO/$(BuildPlatform)'
-    OverWrite: true
-    flattenFolders: true
-  condition: and(succeeded(), eq(variables['PGOBuildMode'], 'Instrument'))
-
-- task: PublishBuildArtifacts@1
-  displayName: 'Publish All PGO Artifacts'
-  inputs:
-    PathtoPublish: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/PGO'
-    ArtifactName: 'PGO'
-  condition: and(succeeded(), eq(variables['PGOBuildMode'], 'Instrument'))
-
-- task: PublishBuildArtifacts@1
-  displayName: 'Publish Artifact: binlog'
-  condition: always()
-  continueOnError: True
-  inputs:
-    PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog
-    ArtifactName: binlog-$(BuildPlatform)
diff --git a/build/pipelines/templates/check-formatting.yml b/build/pipelines/templates/check-formatting.yml
deleted file mode 100644
index 46abca03240..00000000000
--- a/build/pipelines/templates/check-formatting.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-jobs:
-- job: CodeFormatCheck
-  displayName: Proper Code Formatting Check
-  pool: { vmImage: windows-2022 }
-
-  steps:
-  - checkout: self
-    fetchDepth: 1
-    submodules: false
-    clean: true
-
-  - task: PowerShell@2
-    displayName: 'Code Formatting Check'
-    inputs:
-      targetType: filePath
-      filePath: '.\build\scripts\Invoke-FormattingCheck.ps1'
diff --git a/build/pipelines/templates/console-ci-helix-job.yml b/build/pipelines/templates/console-ci-helix-job.yml
deleted file mode 100644
index 07476712a02..00000000000
--- a/build/pipelines/templates/console-ci-helix-job.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-parameters:
-  configuration: 'Release'
-  platform: ''
-  minimumExpectedTestsExecutedCount: 10  # Sanity check for minimum expected tests to be reported
-  rerunPassesRequiredToAvoidFailure: 5
-
-jobs:
-- template: helix-runtests-job.yml
-  parameters:
-    name: 'RunTestsInHelix'
-    # We're not setting dependsOn as we want to rely on the "stage" dependency above us
-    testSuite: 'DevTestSuite'
-    platform: ${{ parameters.platform }}
-    configuration: ${{ parameters.configuration }}
-    rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
-    
-- template: helix-processtestresults-job.yml
-  parameters:
-    dependsOn:
-    - RunTestsInHelix
-    # the default condition is succeededOrFailed(), and the "stage" condition ensures we only run as needed
-    platform: ${{ parameters.platform }}
-    configuration: ${{ parameters.configuration }}
-    rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
-    minimumExpectedTestsExecutedCount: ${{ parameters.minimumExpectedTestsExecutedCount }}
diff --git a/build/pipelines/templates/helix-createprojfile-steps.yml b/build/pipelines/templates/helix-createprojfile-steps.yml
deleted file mode 100644
index 3f7dc8c0977..00000000000
--- a/build/pipelines/templates/helix-createprojfile-steps.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-parameters:
-  condition: ''
-  testFilePath: ''
-  outputProjFileName: ''
-  testSuite: ''
-  taefQuery: ''
-
-steps:
-  - task: powershell@2
-    displayName: 'Create ${{ parameters.outputProjFileName }}'
-    condition: ${{ parameters.condition }}
-    inputs:
-      targetType: filePath
-      filePath: build\Helix\GenerateTestProjFile.ps1
-      arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\$(BuildPlatform)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.60.210621002\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'
\ No newline at end of file
diff --git a/build/pipelines/templates/pgo-merge-pgd-job.yml b/build/pipelines/templates/pgo-merge-pgd-job.yml
deleted file mode 100644
index 89cf5319ccd..00000000000
--- a/build/pipelines/templates/pgo-merge-pgd-job.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-parameters:
-  dependsOn: ''
-  pgoArtifact: PGO
-  platform: ''
-  configuration: ''
-
-jobs:
-- job: MergePGD
-  dependsOn: ${{ parameters.dependsOn }}
-  pool:
-    vmImage: 'windows-2019'
-  variables:
-    artifactsPath: $(Build.SourcesDirectory)\Artifacts
-    pgoArtifactsPath: $(artifactsPath)\${{ parameters.pgoArtifact }}
-    buildPlatform: ${{ parameters.platform }}
-    buildConfiguration: ${{ parameters.configuration }}
-
-  steps:
-  # The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves.
-  - script: |
-      "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt
-      set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt
-      del %TEMP%\vsinstalldir.txt
-      call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat"
-      echo VCToolsInstallDir = %VCToolsInstallDir%
-      echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
-    displayName: 'Retrieve VC tools directory'
-
-  - task: NuGetToolInstaller@0
-    displayName: 'Use NuGet 6.3.0'
-    inputs:
-      versionSpec: 6.3.0
-
-  - task: NuGetAuthenticate@0
-
-  # In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
-  # This should be `task: NuGetCommand@2`
-  - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
-    displayName: Restore NuGet packages for extraneous build actions
-    inputs:
-      command: restore
-      feedsToUse: config
-      configPath: NuGet.config
-      restoreSolution: build/packages.config
-      restoreDirectory: '$(Build.SourcesDirectory)\packages'
-
-  - task: DownloadBuildArtifacts@0
-    inputs:
-      artifactName: ${{ parameters.pgoArtifact }}
-      downloadPath: $(artifactsPath)
-
-  - task: MSBuild@1
-    displayName: Merge counts into PGD
-    inputs:
-      solution: $(Build.SourcesDirectory)\OpenConsole.sln
-      platform: $(buildPlatform)
-      configuration: $(buildConfiguration)
-      msbuildArguments: '/t:MergePGOCounts /p:PGOBuildMode=Instrument /p:PGDPath=$(pgoArtifactsPath)\$(buildPlatform) /p:PGCRootPath=$(pgoArtifactsPath)\$(buildPlatform)'
-
-  - task: CopyFiles@2
-    displayName: 'Copy merged pgd to artifact staging'
-    inputs:
-      sourceFolder: $(pgoArtifactsPath)
-      contents: '**\$(buildPlatform)\*.pgd'
-      targetFolder: $(Build.ArtifactStagingDirectory)
-
-  - task: PublishBuildArtifacts@1
-    inputs:
-      pathToPublish: $(Build.ArtifactStagingDirectory)
-      artifactName: ${{ parameters.pgoArtifact }}
diff --git a/build/scripts/Run-Tests.ps1 b/build/scripts/Run-Tests.ps1
index 1e52a8757e2..bd4cf28fc50 100644
--- a/build/scripts/Run-Tests.ps1
+++ b/build/scripts/Run-Tests.ps1
@@ -9,7 +9,8 @@ Param(
     [Parameter(Mandatory=$false, Position=3)]
     [string]$LogPath,
     [Parameter(Mandatory=$false)]
-    [string]$Root = ".\bin\$Platform\$Configuration"
+    [string]$Root = ".\bin\$Platform\$Configuration",
+    [string[]]$AdditionalTaefArguments
 )
 
 # Find test DLLs based on the provided root, match pattern, and recursion
@@ -26,7 +27,7 @@ if ($LogPath) {
 }
 
 # Invoke the te.exe executable with arguments and test DLLs
-& "$Root\te.exe" $args $testDlls.FullName
+& "$Root\te.exe" $args $testDlls.FullName $AdditionalTaefArguments
 
 # Check the exit code of the te.exe process and exit accordingly
 if ($LASTEXITCODE -ne 0) {
diff --git a/src/cascadia/WpfTerminalControl/WpfTerminalControl.csproj b/src/cascadia/WpfTerminalControl/WpfTerminalControl.csproj
index 661f676b59d..dc3eebe8749 100644
--- a/src/cascadia/WpfTerminalControl/WpfTerminalControl.csproj
+++ b/src/cascadia/WpfTerminalControl/WpfTerminalControl.csproj
@@ -46,6 +46,19 @@
         <Pack>true</Pack>
         <PackagePath>runtimes\win-arm64\native\</PackagePath>
       </None>
+
+      <None Include="$(RepoBinPath)\Win32\$(Configuration)\PublicTerminalCore.pdb">
+        <Pack>true</Pack>
+        <PackagePath>runtimes\win-x86\native\</PackagePath>
+      </None>
+      <None Include="$(RepoBinPath)\x64\$(Configuration)\PublicTerminalCore.pdb">
+        <Pack>true</Pack>
+        <PackagePath>runtimes\win-x64\native\</PackagePath>
+      </None>
+      <None Include="$(RepoBinPath)\ARM64\$(Configuration)\PublicTerminalCore.pdb">
+        <Pack>true</Pack>
+        <PackagePath>runtimes\win-arm64\native\</PackagePath>
+      </None>
     </ItemGroup>
   </Target>
 
diff --git a/src/common.nugetversions.props b/src/common.nugetversions.props
index 19d169a45a0..15e2fe774da 100644
--- a/src/common.nugetversions.props
+++ b/src/common.nugetversions.props
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <!-- PGO -->
-  <Import Condition="'$(PgoTarget)' == 'true' And '$(Platform)'=='x64' And '$(PGOBuildMode)'!='' And Exists('$(SolutionDir)\build\PGO\Terminal.PGO.props')" Project="$(SolutionDir)\build\PGO\Terminal.PGO.props" />
+  <Import Condition="'$(PgoTarget)' == 'true' And ('$(Platform)'=='x64' or '$(Platform)'=='arm64') And '$(PGOBuildMode)'!='' And Exists('$(SolutionDir)\build\PGO\Terminal.PGO.props')" Project="$(SolutionDir)\build\PGO\Terminal.PGO.props" />
 
   <!-- CppWinrt -->
   <Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.CppWinRT.2.0.230207.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="'$(TerminalCppWinrt)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.CppWinRT.2.0.230207.1\build\native\Microsoft.Windows.CppWinRT.props')" />
diff --git a/src/common.nugetversions.targets b/src/common.nugetversions.targets
index 3113c124072..2a3d5cc062e 100644
--- a/src/common.nugetversions.targets
+++ b/src/common.nugetversions.targets
@@ -37,7 +37,8 @@
 
   <ImportGroup Label="ExtensionTargets">
     <!--  PGO -->
-    <Import Condition="'$(PgoTarget)' == 'true' And '$(Platform)'=='x64' And '$(PGOBuildMode)'!='' and Exists('$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets')" Project="$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets" />
+    <Import Condition="'$(PgoTarget)' == 'true' And ('$(Platform)'=='x64' or '$(Platform)'=='arm64') And '$(PGOBuildMode)'!='' and Exists('$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.props')" Project="$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.props" />
+    <Import Condition="'$(PgoTarget)' == 'true' And ('$(Platform)'=='x64' or '$(Platform)'=='arm64') And '$(PGOBuildMode)'!='' and Exists('$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets')" Project="$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets" />
 
     <!-- CppWinrt -->
     <Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.CppWinRT.2.0.230207.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="'$(TerminalCppWinrt)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.CppWinRT.2.0.230207.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
@@ -71,8 +72,8 @@
     </PropertyGroup>
 
     <!--  PGO -->
-    <Import Condition="'$(PgoTarget)' == 'true' And '$(Platform)'=='x64' And '$(PGOBuildMode)'!='' AND !Exists('$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.props')" Text="$([System.String]::Format('$(ErrorText)', '$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.props))" />
-    <Import Condition="'$(PgoTarget)' == 'true' And '$(Platform)'=='x64' And '$(PGOBuildMode)'!='' AND !Exists('$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets))" />
+    <Error Condition="'$(PgoTarget)' == 'true' And ('$(Platform)'=='x64' or '$(Platform)'=='arm64') And '$(PGOBuildMode)'!='' AND !Exists('$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.props')" Text="$([System.String]::Format('$(ErrorText)', '$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.props))" />
+    <Error Condition="'$(PgoTarget)' == 'true' And ('$(Platform)'=='x64' or '$(Platform)'=='arm64') And '$(PGOBuildMode)'!='' AND !Exists('$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(PkgMicrosoft_PGO_Helpers_Cpp)\build\Microsoft.PGO-Helpers.Cpp.targets))" />
 
     <!-- CppWinrt -->
     <Error Condition="'$(TerminalCppWinrt)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.CppWinRT.2.0.230207.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.CppWinRT.2.0.230207.1\build\native\Microsoft.Windows.CppWinRT.props'))" />


Test patch:
diff --git a/build/Helix/runtests.cmd b/build/Helix/runtests.cmd
deleted file mode 100644
index 5c543a141bf..00000000000
--- a/build/Helix/runtests.cmd
+++ /dev/null
@@ -1,105 +0,0 @@
-setlocal ENABLEDELAYEDEXPANSION
-
-echo %TIME%
-
-robocopy %HELIX_CORRELATION_PAYLOAD% . /s /NP > NUL
-
-echo %TIME%
-
-reg add HKLM\Software\Policies\Microsoft\Windows\Appx /v AllowAllTrustedApps /t REG_DWORD /d 1 /f
-
-rem enable dump collection for our test apps:
-rem note, this script is run from a 32-bit cmd, but we need to set the native reg-key
-FOR %%A IN (TestHostApp.exe,te.exe,te.processhost.exe,conhost.exe,OpenConsole.exe,WindowsTerminal.exe) DO (
-  %systemroot%\sysnative\cmd.exe /c reg add "HKLM\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\%%A" /v DumpFolder /t REG_EXPAND_SZ /d %HELIX_DUMP_FOLDER% /f
-  %systemroot%\sysnative\cmd.exe /c reg add "HKLM\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\%%A" /v DumpType /t REG_DWORD /d 2 /f
-  %systemroot%\sysnative\cmd.exe /c reg add "HKLM\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\%%A" /v DumpCount /t REG_DWORD /d 10 /f
-)
-
-echo %TIME%
-
-:: kill dhandler, which is a tool designed to handle unexpected windows appearing. But since our tests are
-:: expected to show UI we don't want it running.
-taskkill -f -im dhandler.exe
-
-echo %TIME%
-powershell -ExecutionPolicy Bypass .\EnsureMachineState.ps1
-echo %TIME%
-powershell -ExecutionPolicy Bypass .\InstallTestAppDependencies.ps1
-echo %TIME%
-
-set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Conhost.UIA.Tests.dll WindowsTerminal.UIA.Tests.dll
-set testBinaries=
-for %%B in (%testBinaryCandidates%) do (
-    if exist %%B (
-        set "testBinaries=!testBinaries! %%B"
-    )
-)
-
-echo %TIME%
-te.exe %testBinaries% /enablewttlogging /unicodeOutput:false /sessionTimeout:0:15 /testtimeout:0:10 /screenCaptureOnError %*
-echo %TIME%
-
-powershell -ExecutionPolicy Bypass Get-Process
-
-move te.wtl te_original.wtl
-
-copy /y te_original.wtl %HELIX_WORKITEM_UPLOAD_ROOT%
-copy /y WexLogFileOutput\*.jpg %HELIX_WORKITEM_UPLOAD_ROOT%
-copy /y *.pgc %HELIX_WORKITEM_UPLOAD_ROOT%
-
-set FailedTestQuery=
-for /F "tokens=* usebackq" %%I IN (`powershell -ExecutionPolicy Bypass .\OutputFailedTestQuery.ps1 te_original.wtl`) DO (
-  set FailedTestQuery=%%I
-)
-
-rem The first time, we'll just re-run failed tests once.  In many cases, tests fail very rarely, such that
-rem a single re-run will be sufficient to detect many unreliable tests.
-if "%FailedTestQuery%" == "" goto :SkipReruns
-
-echo %TIME%
-te.exe %testBinaries% /enablewttlogging /unicodeOutput:false /sessionTimeout:0:15 /testtimeout:0:10 /screenCaptureOnError /select:"%FailedTestQuery%"
-echo %TIME%
-
-move te.wtl te_rerun.wtl
-
-copy /y te_rerun.wtl %HELIX_WORKITEM_UPLOAD_ROOT%
-copy /y WexLogFileOutput\*.jpg %HELIX_WORKITEM_UPLOAD_ROOT%
-
-rem If there are still failing tests remaining, we'll run them eight more times, so they'll have been run a total of ten times.
-rem If any tests fail all ten times, we can be pretty confident that these are actual test failures rather than unreliable tests.
-if not exist te_rerun.wtl goto :SkipReruns
-
-set FailedTestQuery=
-for /F "tokens=* usebackq" %%I IN (`powershell -ExecutionPolicy Bypass .\OutputFailedTestQuery.ps1 te_rerun.wtl`) DO (
-  set FailedTestQuery=%%I
-)
-
-if "%FailedTestQuery%" == "" goto :SkipReruns
-
-echo %TIME%
-te.exe %testBinaries% /enablewttlogging /unicodeOutput:false /sessionTimeout:0:15 /testtimeout:0:10 /screenCaptureOnError /testmode:Loop /LoopTest:8 /select:"%FailedTestQuery%"
-echo %TIME%
-
-powershell -ExecutionPolicy Bypass Get-Process
-
-move te.wtl te_rerun_multiple.wtl
-
-copy /y te_rerun_multiple.wtl %HELIX_WORKITEM_UPLOAD_ROOT%
-copy /y WexLogFileOutput\*.jpg %HELIX_WORKITEM_UPLOAD_ROOT%
-powershell -ExecutionPolicy Bypass .\CopyVisualTreeVerificationFiles.ps1
-
-:SkipReruns
-
-powershell -ExecutionPolicy Bypass Get-Process
-
-echo %TIME%
-powershell -ExecutionPolicy Bypass .\OutputSubResultsJsonFiles.ps1 te_original.wtl te_rerun.wtl te_rerun_multiple.wtl %testnameprefix%
-powershell -ExecutionPolicy Bypass .\ConvertWttLogToXUnit.ps1 te_original.wtl te_rerun.wtl te_rerun_multiple.wtl testResults.xml %testnameprefix%
-echo %TIME%
-
-copy /y *_subresults.json %HELIX_WORKITEM_UPLOAD_ROOT%
-
-type testResults.xml
-
-echo %TIME%
diff --git a/build/pipelines/templates-v2/job-run-pgo-tests.yml b/build/pipelines/templates-v2/job-run-pgo-tests.yml
new file mode 100644
index 00000000000..5270ca1bf85
--- /dev/null
+++ b/build/pipelines/templates-v2/job-run-pgo-tests.yml
@@ -0,0 +1,83 @@
+parameters:
+  buildConfiguration: 'Release'
+  buildPlatform: ''
+  artifactStem: ''
+  testLogPath: '$(Build.BinariesDirectory)\$(BuildPlatform)\$(BuildConfiguration)\testsOnBuildMachine.wtl'
+
+jobs:
+- job: PGO${{ parameters.buildPlatform }}${{ parameters.buildConfiguration }}
+  displayName: PGO ${{ parameters.buildPlatform }} ${{ parameters.buildConfiguration }}
+  variables:
+    BuildConfiguration: ${{ parameters.buildConfiguration }}
+    BuildPlatform: ${{ parameters.buildPlatform }}
+    OutputBuildPlatform: ${{ parameters.buildPlatform }}
+    Terminal.BinDir: $(Build.SourcesDirectory)/bin/$(OutputBuildPlatform)/$(BuildConfiguration)
+  pool:
+    ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+      ${{ if ne(parameters.buildPlatform, 'ARM64') }}:
+        name: SHINE-OSS-Testing-x64
+      ${{ else }}:
+        name: SHINE-OSS-Testing-arm64
+    ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
+      ${{ if ne(parameters.buildPlatform, 'ARM64') }}:
+        name: SHINE-INT-Testing-x64
+      ${{ else }}:
+        name: SHINE-INT-Testing-arm64
+
+  steps:
+  - checkout: self
+    submodules: false
+    clean: true
+    fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
+
+  - task: DownloadPipelineArtifact@2
+    displayName: Download artifacts
+    inputs:
+      artifactName: build-${{ parameters.buildPlatform }}-$(BuildConfiguration)${{ parameters.artifactStem }}
+      downloadPath: $(Terminal.BinDir)
+
+  # The tests expect Terminal to be an unpackaged distribution named terminal-0.0.1.0 (after the dev build version scheme)
+  # Extract to that folder explicitly and strip the embedded folder name from the unpackaged archive.
+  - powershell: |-
+      $TargetDirectory = New-Item -Type Directory (Join-Path "$(Terminal.BinDir)" "terminal-0.0.1.0")
+      & tar.exe -x -v -f (Get-Item "$(Terminal.BinDir)/_unpackaged/*.zip") -C $TargetDirectory.FullName --strip-components=1
+    displayName: Extract the unpackaged build for PGO
+
+  - template: steps-ensure-nuget-version.yml
+
+  - powershell: |-
+      $Package = 'Microsoft.Internal.Windows.Terminal.TestContent'
+      $Version = '1.0.1'
+      & nuget.exe install $Package -Version $Version
+      Write-Host "##vso[task.setvariable variable=TerminalTestContentPath]$(Build.SourcesDirectory)\packages\${Package}.${Version}\content"
+    displayName: Install Test Content
+
+  - task: PowerShell@2
+    displayName: 'Run PGO Tests'
+    inputs:
+      targetType: filePath
+      filePath: build\scripts\Run-Tests.ps1
+      arguments: >-
+        -MatchPattern '*UIA.Tests.dll'
+        -Platform '$(OutputBuildPlatform)'
+        -Configuration '$(BuildConfiguration)'
+        -LogPath '${{ parameters.testLogPath }}'
+        -Root "$(Terminal.BinDir)"
+        -AdditionalTaefArguments '/select:(@IsPGO=true)','/p:WTTestContent=$(TerminalTestContentPath)'
+    condition: and(succeeded(), ne(variables['PGOBuildMode'], 'Instrument'))
+
+  - task: CopyFiles@2
+    displayName: 'Copy PGO outputs to Artifacts'
+    condition: always()
+    inputs:
+      Contents: |
+       **/*.pgc
+       ${{ parameters.testLogPath }}
+      TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/pgc'
+      OverWrite: true
+      flattenFolders: true
+
+  - publish: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/pgc'
+    artifact: pgc-intermediates-$(BuildPlatform)-$(BuildConfiguration)${{ parameters.artifactStem }}
+    condition: always()
diff --git a/build/pipelines/templates/test-console-ci.yml b/build/pipelines/templates-v2/job-test-project.yml
similarity index 72%
rename from build/pipelines/templates/test-console-ci.yml
rename to build/pipelines/templates-v2/job-test-project.yml
index 5a7db6475bd..483a32893ce 100644
--- a/build/pipelines/templates/test-console-ci.yml
+++ b/build/pipelines/templates-v2/job-test-project.yml
@@ -1,9 +1,8 @@
 parameters:
   configuration: 'Release'
   platform: ''
-  additionalBuildArguments: ''
-  artifactName: 'drop'
   testLogPath: '$(Build.BinariesDirectory)\$(BuildPlatform)\$(BuildConfiguration)\testsOnBuildMachine.wtl'
+  artifactStem: ''
 
 jobs:
 - job: Test${{ parameters.platform }}${{ parameters.configuration }}
@@ -11,6 +10,11 @@ jobs:
   variables:
     BuildConfiguration: ${{ parameters.configuration }}
     BuildPlatform: ${{ parameters.platform }}
+    ${{ if eq(parameters.platform, 'x86') }}:
+      OutputBuildPlatform: Win32
+    ${{ else }}:
+      OutputBuildPlatform: ${{ parameters.platform }}
+    Terminal.BinDir: $(Build.SourcesDirectory)/bin/$(OutputBuildPlatform)/$(BuildConfiguration)
   pool:
     ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
       ${{ if ne(parameters.platform, 'ARM64') }}:
@@ -28,26 +32,20 @@ jobs:
     submodules: false
     clean: true
     fetchDepth: 1
+    fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
 
-  - task: DownloadBuildArtifacts@0
+  - task: DownloadPipelineArtifact@2
+    displayName: Download artifacts
     inputs:
-      artifactName: ${{ parameters.artifactName }}
-
-  - task: PowerShell@2
-    displayName: 'Rationalize build platform'
-    inputs:
-      targetType: inline
-      script: |
-        $Arch = "$(BuildPlatform)"
-        If ($Arch -Eq "x86") { $Arch = "Win32" }
-        Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
+      artifactName: build-${{ parameters.platform }}-$(BuildConfiguration)${{ parameters.artifactStem }}
+      downloadPath: $(Terminal.BinDir)
 
   - task: PowerShell@2
     displayName: 'Run Unit Tests'
     inputs:
       targetType: filePath
       filePath: build\scripts\Run-Tests.ps1
-      arguments: -MatchPattern '*unit.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}' -Root "$(System.ArtifactsDirectory)\\${{ parameters.artifactName }}\\$(BuildConfiguration)\\$(BuildPlatform)\\test"
+      arguments: -MatchPattern '*unit.test*.dll' -Platform '$(OutputBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}' -Root "$(Terminal.BinDir)"
     condition: and(succeeded(), ne(variables['PGOBuildMode'], 'Instrument'))
 
   - ${{ if or(eq(parameters.platform, 'x64'), eq(parameters.platform, 'arm64')) }}:
@@ -56,7 +54,7 @@ jobs:
       inputs:
         targetType: filePath
         filePath: build\scripts\Run-Tests.ps1
-        arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}' -Root "$(System.ArtifactsDirectory)\\${{ parameters.artifactName }}\\$(BuildConfiguration)\\$(BuildPlatform)\\test"
+        arguments: -MatchPattern '*feature.test*.dll' -Platform '$(OutputBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}' -Root "$(Terminal.BinDir)"
       condition: and(succeeded(), ne(variables['PGOBuildMode'], 'Instrument'))
 
   - task: PowerShell@2
@@ -89,4 +87,4 @@ jobs:
       flattenFolders: true
 
   - publish: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/test-logs'
-    artifact: TestLogs$(BuildPlatform)$(BuildConfiguration)
+    artifact: test-logs-$(BuildPlatform)-$(BuildConfiguration)${{ parameters.artifactStem }}
diff --git a/build/pipelines/templates/helix-processtestresults-job.yml b/build/pipelines/templates/helix-processtestresults-job.yml
deleted file mode 100644
index bd8e967de92..00000000000
--- a/build/pipelines/templates/helix-processtestresults-job.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-parameters:
-  condition: 'succeededOrFailed()'
-  dependsOn: ''
-  rerunPassesRequiredToAvoidFailure: 5
-  minimumExpectedTestsExecutedCount: 10
-  checkJobAttempt: false
-  pgoArtifact: ''
-
-jobs:
-- job: ProcessTestResults
-  displayName: Process Helix Results ${{ parameters.platform }} ${{ parameters.configuration }}
-  condition: ${{ parameters.condition }}
-  dependsOn: ${{ parameters.dependsOn }}
-  pool:
-    vmImage: 'windows-2019'
-  timeoutInMinutes: 120
-  variables:
-    helixOutputFolder: $(Build.SourcesDirectory)\HelixOutput
-
-  steps:
-  - task: powershell@2
-    displayName: 'UpdateUnreliableTests.ps1'
-    condition: succeededOrFailed()
-    env:
-      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-      HelixAccessToken: $(HelixApiAccessToken)
-    inputs:
-      targetType: filePath
-      filePath: build\Helix\UpdateUnreliableTests.ps1
-      arguments: -RerunPassesRequiredToAvoidFailure '${{ parameters.rerunPassesRequiredToAvoidFailure }}'
-
-  - task: powershell@2
-    displayName: 'OutputTestResults.ps1'
-    condition: succeededOrFailed()
-    env:
-      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-      HelixAccessToken: $(HelixApiAccessToken)
-    inputs:
-      targetType: filePath
-      filePath: build\Helix\OutputTestResults.ps1
-      arguments: -MinimumExpectedTestsExecutedCount '${{ parameters.minimumExpectedTestsExecutedCount }}' -CheckJobAttempt $${{ parameters.checkJobAttempt }}
-
-  - task: powershell@2
-    displayName: 'ProcessHelixFiles.ps1'
-    condition: succeededOrFailed()
-    env:
-      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-      HelixAccessToken: $(HelixApiAccessToken)
-    inputs:
-      targetType: filePath
-      filePath: build\Helix\ProcessHelixFiles.ps1
-      arguments: -OutputFolder '$(helixOutputFolder)'
-
-  - ${{if ne(parameters.pgoArtifact, '') }}:
-    - script: move /y $(helixOutputFolder)\PGO $(Build.ArtifactStagingDirectory)
-      displayName: 'Move pgc files to PGO artifact'
-
-  - task: PublishBuildArtifacts@1
-    displayName: 'Publish Helix files'
-    condition: succeededOrFailed()
-    inputs:
-      PathtoPublish: $(helixOutputFolder)
-      artifactName: drop
-
-  - ${{if ne(parameters.pgoArtifact, '') }}:
-    - task: PublishBuildArtifacts@1
-      displayName: 'Publish pgc files'
-      condition: succeededOrFailed()
-      inputs:
-        PathtoPublish: $(Build.ArtifactStagingDirectory)\PGO\Release
-        artifactName: ${{ parameters.pgoArtifact }}
diff --git a/build/pipelines/templates/helix-runtests-job.yml b/build/pipelines/templates/helix-runtests-job.yml
deleted file mode 100644
index 0e7c22efff0..00000000000
--- a/build/pipelines/templates/helix-runtests-job.yml
+++ /dev/null
@@ -1,164 +0,0 @@
-parameters:
-  name: 'RunTestsInHelix'
-  dependsOn: ''
-  condition: ''
-  testSuite: ''
-  # If a Pipeline runs this template more than once, this parameter should be unique per build flavor to differentiate the
-  # the different test runs:
-  helixType: 'test/devtest'
-  artifactName: 'drop'
-  maxParallel: 4
-  rerunPassesRequiredToAvoidFailure: 5
-  taefQuery: ''
-  configuration: ''
-  platform: ''
-  # if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline:
-  useBuildOutputFromPipeline: $(System.DefinitionId)
-  openHelixTargetQueues: 'windows.11.amd64.client.open.reunion'
-  closedHelixTargetQueues: 'windows.11.amd64.client.reunion'
-
-jobs:
-- job: ${{ parameters.name }}
-  displayName: Submit Helix ${{ parameters.platform }} ${{ parameters.configuration }}
-  dependsOn: ${{ parameters.dependsOn }}
-  condition: ${{ parameters.condition }}
-  pool:
-    vmImage: 'windows-2019'
-  timeoutInMinutes: 120
-  strategy:
-    maxParallel: ${{ parameters.maxParallel }}
-  variables:
-    buildConfiguration: ${{ parameters.configuration }}
-    buildPlatform: ${{ parameters.platform }}
-    openHelixTargetQueues: ${{ parameters.openHelixTargetQueues }}
-    closedHelixTargetQueues: ${{ parameters.closedHelixTargetQueues }}
-    artifactsDir: $(Build.SourcesDirectory)\Artifacts
-    taefPath: $(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.60.210621002\build\Binaries\$(buildPlatform)
-    helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}'
-
-  steps:
-  - task: CmdLine@1
-    displayName: 'Display build machine environment variables'
-    inputs:
-      filename: 'set'
-
-  - task: NuGetToolInstaller@0
-    displayName: 'Use NuGet 6.3.0'
-    inputs:
-      versionSpec: 6.3.0
-
-  - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
-    displayName: 'NuGet restore build/Helix/packages.config'
-    inputs:
-      restoreSolution: build/Helix/packages.config
-      feedsToUse: config
-      nugetConfigPath: nuget.config
-      restoreDirectory: packages
-
-  - task: DownloadBuildArtifacts@0
-    condition:
-      and(succeeded(),eq(variables['useBuildOutputFromBuildId'],''))
-    inputs:
-      artifactName: ${{ parameters.artifactName }}
-      downloadPath: '$(artifactsDir)'
-
-  - task: DownloadBuildArtifacts@0
-    condition:
-      and(succeeded(),ne(variables['useBuildOutputFromBuildId'],''))
-    inputs:
-      buildType: specific
-      buildVersionToDownload: specific
-      project: $(System.TeamProjectId)
-      pipeline: ${{ parameters.useBuildOutputFromPipeline }}
-      buildId: $(useBuildOutputFromBuildId)
-      artifactName: ${{ parameters.artifactName }}
-      downloadPath: '$(artifactsDir)'
-
-  - task: CmdLine@1
-    displayName: 'Display Artifact Directory payload contents'
-    inputs:
-      filename: 'dir'
-      arguments: '/s $(artifactsDir)'
-
-  - task: powershell@2
-    displayName: 'PrepareHelixPayload.ps1'
-    inputs:
-      targetType: filePath
-      filePath: build\Helix\PrepareHelixPayload.ps1
-      arguments: -Platform '$(buildPlatform)' -Configuration '$(buildConfiguration)' -ArtifactName '${{ parameters.artifactName }}'
-
-  - task: CmdLine@1
-    displayName: 'Display Helix payload contents'
-    inputs:
-      filename: 'dir'
-      arguments: '/s $(Build.SourcesDirectory)\HelixPayload'
-
-  - task: PowerShell@2
-    displayName: 'Make artifact directories'
-    inputs:
-      targetType: inline
-      script: |
-        New-Item -ItemType Directory -Force -Path "$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\"
-        New-Item -ItemType Directory -Force -Path "$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\$(BuildPlatform)\"
-
-  - template: helix-createprojfile-steps.yml
-    parameters:
-      condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
-      testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\TerminalApp.LocalTests.dll'
-      outputProjFileName: 'RunTestsInHelix-TerminalAppLocalTests.proj'
-      testSuite: '${{ parameters.testSuite }}'
-      taefQuery: ${{ parameters.taefQuery }}
-
-  - template: helix-createprojfile-steps.yml
-    parameters:
-      condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
-      testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\SettingsModel.LocalTests.dll'
-      outputProjFileName: 'RunTestsInHelix-SettingsModelLocalTests.proj'
-      testSuite: '${{ parameters.testSuite }}'
-      taefQuery: ${{ parameters.taefQuery }}
-
-
-  - template: helix-createprojfile-steps.yml
-    parameters:
-      condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
-      testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\Conhost.UIA.Tests.dll'
-      outputProjFileName: 'RunTestsInHelix-HostTestsUIA.proj'
-      testSuite: '${{ parameters.testSuite }}'
-      taefQuery: ${{ parameters.taefQuery }}
-
-  - template: helix-createprojfile-steps.yml
-    parameters:
-      condition: and(succeeded(),or(eq('${{ parameters.testSuite }}','PgoInstrumentationSuite'),eq('${{ parameters.testSuite }}','DevTestSuite')))
-      testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\WindowsTerminal.UIA.Tests.dll'
-      outputProjFileName: 'RunTestsInHelix-WindowsTerminalUIATests.proj'
-      testSuite: '${{ parameters.testSuite }}'
-      taefQuery: ${{ parameters.taefQuery }}
-
-  - task: PublishBuildArtifacts@1
-    displayName: 'Publish generated .proj files'
-    inputs:
-      PathtoPublish: $(Build.ArtifactStagingDirectory)
-      artifactName: ${{ parameters.artifactName }}
-
-  - task: DotNetCoreCLI@2
-    displayName: 'Run tests in Helix (open queues)'
-    condition: and(succeeded(),eq(variables['System.CollectionUri'],'https://dev.azure.com/ms/'))
-    env:
-      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-    inputs:
-      command: custom
-      projects: build\Helix\RunTestsInHelix.proj
-      custom: msbuild
-      arguments: '$(helixCommonArgs) /p:IsExternal=true /p:Creator=Terminal /p:HelixTargetQueues=$(openHelixTargetQueues)'
-
-  - task: DotNetCoreCLI@2
-    displayName: 'Run tests in Helix (closed queues)'
-    condition: and(succeeded(),ne(variables['System.CollectionUri'],'https://dev.azure.com/ms/'))
-    env:
-      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-      HelixAccessToken: $(HelixApiAccessToken)
-    inputs:
-      command: custom
-      projects: build\Helix\RunTestsInHelix.proj
-      custom: msbuild
-      arguments: '$(helixCommonArgs) /p:HelixTargetQueues=$(closedHelixTargetQueues)'