<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: KAMEGAWA Kazushi</title>
    <description>The latest articles on DEV Community by KAMEGAWA Kazushi (@kkamegawa).</description>
    <link>https://dev.to/kkamegawa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F468624%2F471e4ca1-5e06-48b4-84da-d7bd0f418560.jpeg</url>
      <title>DEV Community: KAMEGAWA Kazushi</title>
      <link>https://dev.to/kkamegawa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kkamegawa"/>
    <language>en</language>
    <item>
      <title>Make Intune App Publishing Much Easier with Relaypublisher</title>
      <dc:creator>KAMEGAWA Kazushi</dc:creator>
      <pubDate>Sun, 25 Oct 2026 07:37:42 +0000</pubDate>
      <link>https://dev.to/kkamegawa/make-intune-app-publishing-much-easier-with-relaypublisher-ch4</link>
      <guid>https://dev.to/kkamegawa/make-intune-app-publishing-much-easier-with-relaypublisher-ch4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I originally wrote this article in Japanese, and I used AI to translate it into English. You can read the original post here: &lt;a href="https://kkamegawa.hatenablog.jp/entry/2026/10/25/162550" rel="noopener noreferrer"&gt;RelaypublisherでIntuneへのアプリ登録を劇的に改善する&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uploading Windows and macOS apps to Intune one by one is painful. At the end of August, I built a tool to fix this and started using it. In my environment, it has made things much easier.&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/kkamegawa/i-built-relaypublisher-a-winget-like-publishing-workflow-for-microsoft-intune-1oep" class="crayons-story__hidden-navigation-link"&gt;I Built RelayPublisher: A winget-like Publishing Workflow for Microsoft Intune&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/kkamegawa" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F468624%2F471e4ca1-5e06-48b4-84da-d7bd0f418560.jpeg" alt="kkamegawa profile" class="crayons-avatar__image" width="460" height="460"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/kkamegawa" class="crayons-story__secondary fw-medium m:hidden"&gt;
              KAMEGAWA Kazushi
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                KAMEGAWA Kazushi
                
                
              
              &lt;div id="story-author-preview-content-4542425" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/kkamegawa" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F468624%2F471e4ca1-5e06-48b4-84da-d7bd0f418560.jpeg" class="crayons-avatar__image" alt="" width="460" height="460"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;KAMEGAWA Kazushi&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/kkamegawa/i-built-relaypublisher-a-winget-like-publishing-workflow-for-microsoft-intune-1oep" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 1&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/kkamegawa/i-built-relaypublisher-a-winget-like-publishing-workflow-for-microsoft-intune-1oep" id="article-link-4542425"&gt;
          I Built RelayPublisher: A winget-like Publishing Workflow for Microsoft Intune
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dotnet"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dotnet&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/microsoft"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;microsoft&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/intune"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;intune&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/kkamegawa/i-built-relaypublisher-a-winget-like-publishing-workflow-for-microsoft-intune-1oep" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/kkamegawa/i-built-relaypublisher-a-winget-like-publishing-workflow-for-microsoft-intune-1oep#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;I designed the tool, so of course it fits my own needs best. Still, some parts are a bit hard to understand, so I want to explain them in more detail. In this article, I use PowerShell 7 (the MSIX version) as an example. I walk through every step, from writing the manifest to automatic publishing to Intune with Azure Pipelines.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tool: &lt;a href="https://github.com/kkamegawa/Relaypublisher" rel="noopener noreferrer"&gt;kkamegawa/Relaypublisher&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Skill for writing manifests: &lt;a href="https://github.com/tfsugjp/skills/tree/main/plugins/relaypublisher-manifest" rel="noopener noreferrer"&gt;tfsugjp/skills - relaypublisher-manifest&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have written a winget manifest before, the syntax will feel familiar. I made it close to winget on purpose. Even so, you will probably fail a few times at first, so the skill above helps a lot. I run everything on Azure DevOps, so this article uses Azure Pipelines. If you use GitHub Actions, AI can convert the pipeline for you quite easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Register an application in Entra ID&lt;/li&gt;
&lt;li&gt;Write the manifest (YAML). The skill makes this easy.&lt;/li&gt;
&lt;li&gt;Put the YAML files in a private repository (a public one also works, but…). A winget-like folder structure is a good choice.&lt;/li&gt;
&lt;li&gt;Create the CI (Azure Pipelines)&lt;/li&gt;
&lt;li&gt;Get relaypublisher inside the CI&lt;/li&gt;
&lt;li&gt;Merge a PR and test it&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Register an application in Entra ID
&lt;/h2&gt;

&lt;p&gt;relaypublisher registers apps in Intune through Microsoft Graph. So first, I register an application in Entra ID and make it usable from Azure DevOps or GitHub.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Entra admin center, create a new &lt;strong&gt;App registration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;API permissions&lt;/strong&gt;, add the Microsoft Graph &lt;strong&gt;application permissions&lt;/strong&gt; needed for Intune app management, and grant admin consent. (Please check the README for the exact permissions.)&lt;/li&gt;
&lt;li&gt;In Azure DevOps, create an Azure Resource Manager connection under &lt;strong&gt;Service connections&lt;/strong&gt;. I recommend workload identity federation, because you don't need to manage secrets.&lt;/li&gt;
&lt;li&gt;Write down the connection name, because the pipeline uses it. (In this article, it is &lt;code&gt;Microsoft Azure&lt;/code&gt;.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Save the tenant ID as a &lt;strong&gt;protected (secret) pipeline variable&lt;/strong&gt; named &lt;code&gt;EXPECTED_TENANT_ID&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips for writing the YAML
&lt;/h2&gt;

&lt;p&gt;You can write a manifest by hand, but the &lt;strong&gt;&lt;code&gt;relaypublisher-manifest&lt;/code&gt; skill in tfsugjp/skills&lt;/strong&gt; makes it much easier. Load the skill into an agent such as GitHub Copilot or Claude Code, and ask something like "Create a manifest to deploy the PowerShell 7.6.8 MSIX to Intune." It will create a template for you. I ask Copilot to do this every time.&lt;/p&gt;

&lt;p&gt;Some packages, like MSIX, can't become a Win32 app as they are. For these, you will probably need install, uninstall, and detection scripts. On Windows, you create an &lt;code&gt;.intunewin&lt;/code&gt; file with &lt;code&gt;IntuneWinAppUtil.exe&lt;/code&gt;. This tool is not open source, so relaypublisher downloads the latest version each time. It only runs on Windows, so if you build Windows packages, use a CI that has Windows runners.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detection script (detect-x64.ps1)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="s1"&gt;'7.6.8.0'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$architecture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'X64'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-AppxPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AllUsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Microsoft.PowerShell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Architecture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$architecture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$provisioned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-AppxProvisionedPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Online&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisplayName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Microsoft.PowerShell'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PackageName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'_x64_'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$provisioned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'PowerShell 7.6.8 x64 is installed.'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install script (install-msix.ps1)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="bp"&gt;$Error&lt;/span&gt;&lt;span class="n"&gt;ActionPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Stop'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$bundlePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Join-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$PSScriptRoot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'PowerShell-7.6.8.msixbundle'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-LiteralPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$bundlePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PathType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Leaf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PowerShell MSIX bundle not found: &lt;/span&gt;&lt;span class="nv"&gt;$bundlePath&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Add-AppxProvisionedPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Online&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PackagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$bundlePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-SkipLicense&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Failed to provision the PowerShell MSIX bundle: &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Uninstall script (uninstall-msix.ps1)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="bp"&gt;$Error&lt;/span&gt;&lt;span class="n"&gt;ActionPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Stop'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="s1"&gt;'7.6.8.0'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Get-AppxPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AllUsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Microsoft.PowerShell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Remove-AppxPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PackageFullName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AllUsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Get-AppxProvisionedPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Online&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisplayName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Microsoft.PowerShell'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Remove-AppxProvisionedPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Online&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PackageName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PackageName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AllUsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Failed to remove the PowerShell 7.6.8 MSIX package: &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Manifest
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;SchemaVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
&lt;span class="na"&gt;PackageIdentifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Microsoft.PowerShell&lt;/span&gt;
&lt;span class="na"&gt;PackageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell&lt;/span&gt;
&lt;span class="na"&gt;Publisher&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Microsoft Corporation&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell 7 cross-platform automation and configuration tool.&lt;/span&gt;
&lt;span class="na"&gt;PackageVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;7.6.8&lt;/span&gt;
&lt;span class="na"&gt;AssignmentSync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;merge&lt;/span&gt;

&lt;span class="na"&gt;Apps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows&lt;/span&gt;
    &lt;span class="na"&gt;Architecture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;x64&lt;/span&gt;
    &lt;span class="na"&gt;Categories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Data management&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Computer management&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Development &amp;amp; Design&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Productivity&lt;/span&gt;
    &lt;span class="na"&gt;InstallerType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;win32&lt;/span&gt;
    &lt;span class="na"&gt;DisplayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell [Windows x64]&lt;/span&gt;

    &lt;span class="na"&gt;Package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;IntuneWin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;SetupFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell-7.6.8.msixbundle&lt;/span&gt;

      &lt;span class="na"&gt;RepositoryFiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scripts/windows/powershell/7.6.8/install-msix.ps1&lt;/span&gt;
          &lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install-msix.ps1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scripts/windows/powershell/7.6.8/uninstall-msix.ps1&lt;/span&gt;
          &lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uninstall-msix.ps1&lt;/span&gt;

      &lt;span class="na"&gt;ExternalFiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;githubRelease&lt;/span&gt;
          &lt;span class="na"&gt;Owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell&lt;/span&gt;
          &lt;span class="na"&gt;Repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell&lt;/span&gt;
          &lt;span class="na"&gt;Tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v7.6.8&lt;/span&gt;
          &lt;span class="na"&gt;AssetName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell-7.6.8.msixbundle&lt;/span&gt;
          &lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell-7.6.8.msixbundle&lt;/span&gt;
          &lt;span class="na"&gt;Sha256&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A77EF80D069C342739211904B7917769C190DD0C9CCBE492F21D637F318B5E96"&lt;/span&gt;
          &lt;span class="na"&gt;Auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;

    &lt;span class="na"&gt;Install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CommandLine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\install-msix.ps1&lt;/span&gt;
      &lt;span class="na"&gt;UninstallCommandLine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\uninstall-msix.ps1&lt;/span&gt;
      &lt;span class="na"&gt;InstallExperience&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;system&lt;/span&gt;
      &lt;span class="na"&gt;RestartBehavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;suppress&lt;/span&gt;

    &lt;span class="na"&gt;Detection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
      &lt;span class="na"&gt;ScriptFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scripts/windows/powershell/7.6.8/detect-x64.ps1&lt;/span&gt;
      &lt;span class="na"&gt;RunAs32Bit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;EnforceSignatureCheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

    &lt;span class="na"&gt;Requirements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MinimumOSVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.19045&lt;/span&gt;
      &lt;span class="na"&gt;Architecture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;x64&lt;/span&gt;

    &lt;span class="na"&gt;Assignments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;group&lt;/span&gt;
        &lt;span class="na"&gt;GroupId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;object&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ID&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;group&amp;gt;"&lt;/span&gt;
        &lt;span class="na"&gt;Intent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;required&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are the key points.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ExternalFiles&lt;/code&gt;&lt;/strong&gt;: Don't put the installer itself in the repository. Get it from a GitHub Release instead. Always set &lt;code&gt;Sha256&lt;/code&gt;, so a tampered file will never get in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;RepositoryFiles&lt;/code&gt;&lt;/strong&gt;: These scripts from the repository are bundled into the intunewin file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Detection&lt;/code&gt;&lt;/strong&gt;: The detection script is not included in the package. It is registered as an Intune detection rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Assignments&lt;/code&gt;&lt;/strong&gt;: This item is unique to relaypublisher. winget manifests don't have it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Put the YAML in a private repository
&lt;/h2&gt;

&lt;p&gt;A manifest includes things like the IDs of target groups, so I recommend keeping it in a private repository (Azure Repos, for example).&lt;/p&gt;

&lt;p&gt;A folder structure like winget-pkgs makes management easier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;manifests/
  Microsoft/
    PowerShell/
      7.6.8/
        Microsoft.PowerShell.yaml
scripts/
  windows/
    powershell/
      7.6.8/
        install-msix.ps1
        uninstall-msix.ps1
        detect-x64.ps1
.azurepipelines/
  intuneapps.yml
  install-relaypublisher.ps1
  resolve-manifest-base.ps1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The big difference from winget is that &lt;strong&gt;you can choose which groups get the app&lt;/strong&gt;. So if you want to deploy the same app to different groups, split the folders and create a separate manifest for each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;manifests/Microsoft/PowerShell/7.6.8/dev/Microsoft.PowerShell.yaml      # For the dev team (required)
manifests/Microsoft/PowerShell/7.6.8/general/Microsoft.PowerShell.yaml  # For the whole company (available)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, you can see "which app goes to which group" just by looking at the PR diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the CI (Azure Pipelines)
&lt;/h2&gt;

&lt;p&gt;The pipeline has three stages.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Validate&lt;/td&gt;
&lt;td&gt;ubuntu-latest&lt;/td&gt;
&lt;td&gt;Finds changed manifests with &lt;code&gt;plan&lt;/code&gt; and checks them with &lt;code&gt;validate&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;windows-latest&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Creates intunewin files with &lt;code&gt;package&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publish&lt;/td&gt;
&lt;td&gt;ubuntu-latest&lt;/td&gt;
&lt;td&gt;Registers apps in Intune with &lt;code&gt;publish&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For PRs, the pipeline runs in &lt;strong&gt;dry-run&lt;/strong&gt; mode and only checks things. It &lt;strong&gt;publishes for real only when changes are merged into main&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip: Azure Repos users&lt;/strong&gt;&lt;br&gt;
Azure Repos ignores the &lt;code&gt;pr:&lt;/code&gt; trigger in YAML. To run the pipeline on PRs, add it under &lt;strong&gt;Branch policies → Build validation&lt;/strong&gt; for the main branch. For PR builds, &lt;code&gt;Build.Reason&lt;/code&gt; is &lt;code&gt;PullRequest&lt;/code&gt;, so the Publish stage automatically runs in dry-run mode.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Pipeline that publishes manifest changes on main to Intune&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;intuneapps-$(Date:yyyyMMdd).$(Rev:r)&lt;/span&gt;

&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;manifests&lt;/span&gt;

&lt;span class="na"&gt;pr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;

&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Manual runs are dry-run by default. Set this to true only when you want to really publish.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allowManualPublish&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish for real on manual runs (disable dry-run)&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;boolean&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;# If set, Validate / Package are skipped, and only the Publish stage runs again&lt;/span&gt;
  &lt;span class="c1"&gt;# using the manifest-list / intune-packages artifacts from the given run.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sourceBuildId&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build ID of the run to re-publish from (leave empty for a normal run)&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Allow the service connection with this name for this pipeline in Azure DevOps.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;azureServiceConnection&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Microsoft Azure&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${{ if eq(parameters.sourceBuildId, '') }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate manifests&lt;/span&gt;
      &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate&lt;/span&gt;
          &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Plan and validate&lt;/span&gt;
          &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
          &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;checkout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&lt;/span&gt;
              &lt;span class="na"&gt;fetchDepth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UseDotNet@2&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install .NET 10 SDK&lt;/span&gt;
              &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;packageType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sdk&lt;/span&gt;
                &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.x&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NuGetAuthenticate@1&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authenticate Azure Artifacts&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pwsh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;&amp;amp; "$(Build.SourcesDirectory)/.azurepipelines/install-relaypublisher.ps1"&lt;/span&gt;
                &lt;span class="s"&gt;if ($LASTEXITCODE -ne 0) {&lt;/span&gt;
                    &lt;span class="s"&gt;exit $LASTEXITCODE&lt;/span&gt;
                &lt;span class="s"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install latest relaypublisher from Azure Artifacts&lt;/span&gt;
              &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;SYSTEM_ACCESSTOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.AccessToken)&lt;/span&gt;
                &lt;span class="na"&gt;SYSTEM_COLLECTIONURI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.CollectionUri)&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pwsh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;&amp;amp; "$(Build.SourcesDirectory)/.azurepipelines/resolve-manifest-base.ps1"&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Resolve safe manifest base&lt;/span&gt;
              &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;BUILD_REASON&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.Reason)&lt;/span&gt;
                &lt;span class="na"&gt;BUILD_REPOSITORY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.Repository.ID)&lt;/span&gt;
                &lt;span class="na"&gt;BUILD_SOURCEBRANCH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.SourceBranch)&lt;/span&gt;
                &lt;span class="na"&gt;BUILD_SOURCEVERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.SourceVersion)&lt;/span&gt;
                &lt;span class="na"&gt;BUILD_SOURCESDIRECTORY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.SourcesDirectory)&lt;/span&gt;
                &lt;span class="na"&gt;MANIFEST_OVERRIDE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(MANIFEST_OVERRIDE)&lt;/span&gt;
                &lt;span class="na"&gt;SYSTEM_ACCESSTOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.AccessToken)&lt;/span&gt;
                &lt;span class="na"&gt;SYSTEM_COLLECTIONURI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.CollectionUri)&lt;/span&gt;
                &lt;span class="na"&gt;SYSTEM_PULLREQUEST_TARGETBRANCH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.PullRequest.TargetBranch)&lt;/span&gt;
                &lt;span class="na"&gt;SYSTEM_TEAMPROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.TeamProject)&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;bash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;set -euo pipefail&lt;/span&gt;
                &lt;span class="s"&gt;relaypublisher plan \&lt;/span&gt;
                  &lt;span class="s"&gt;--repo-root "$(Build.SourcesDirectory)" \&lt;/span&gt;
                  &lt;span class="s"&gt;--manifest-root manifests \&lt;/span&gt;
                  &lt;span class="s"&gt;--base-ref "$(ManifestBaseRef)" \&lt;/span&gt;
                  &lt;span class="s"&gt;--output "$(Build.ArtifactStagingDirectory)/manifest-list.json"&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Plan changed manifests&lt;/span&gt;
              &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eq(variables['MANIFEST_OVERRIDE'], '')&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pwsh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;$ErrorActionPreference = 'Stop'&lt;/span&gt;
                &lt;span class="s"&gt;$planArguments = @(&lt;/span&gt;
                  &lt;span class="s"&gt;'plan',&lt;/span&gt;
                  &lt;span class="s"&gt;'--repo-root', '$(Build.SourcesDirectory)',&lt;/span&gt;
                  &lt;span class="s"&gt;'--manifest-root', 'manifests',&lt;/span&gt;
                  &lt;span class="s"&gt;'--output', '$(Build.ArtifactStagingDirectory)/manifest-list.json'&lt;/span&gt;
                &lt;span class="s"&gt;)&lt;/span&gt;
                &lt;span class="s"&gt;foreach ($path in ($env:MANIFEST_OVERRIDE_PATHS -split ';')) {&lt;/span&gt;
                    &lt;span class="s"&gt;$planArguments += @('--manifest', $path)&lt;/span&gt;
                &lt;span class="s"&gt;}&lt;/span&gt;
                &lt;span class="s"&gt;&amp;amp; relaypublisher @planArguments&lt;/span&gt;
                &lt;span class="s"&gt;if ($LASTEXITCODE -ne 0) {&lt;/span&gt;
                    &lt;span class="s"&gt;exit $LASTEXITCODE&lt;/span&gt;
                &lt;span class="s"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Plan manually selected manifests&lt;/span&gt;
              &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ne(variables['MANIFEST_OVERRIDE'], '')&lt;/span&gt;
              &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;MANIFEST_OVERRIDE_PATHS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(ManifestOverridePaths)&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;bash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;set -euo pipefail&lt;/span&gt;
                &lt;span class="s"&gt;relaypublisher validate \&lt;/span&gt;
                  &lt;span class="s"&gt;--repo-root "$(Build.SourcesDirectory)" \&lt;/span&gt;
                  &lt;span class="s"&gt;--manifest-list "$(Build.ArtifactStagingDirectory)/manifest-list.json"&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate manifest list&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.ArtifactStagingDirectory)/manifest-list.json&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish manifest list artifact&lt;/span&gt;
              &lt;span class="na"&gt;artifact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manifest-list&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Package&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Package installers&lt;/span&gt;
      &lt;span class="na"&gt;dependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;succeeded()&lt;/span&gt;
      &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PackageWindows&lt;/span&gt;
          &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Package Windows and macOS installers&lt;/span&gt;
          &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows-latest&lt;/span&gt;
          &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;checkout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UseDotNet@2&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install .NET 10 SDK&lt;/span&gt;
              &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;packageType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sdk&lt;/span&gt;
                &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.x&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NuGetAuthenticate@1&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authenticate Azure Artifacts&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pwsh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;&amp;amp; "$(Build.SourcesDirectory)/.azurepipelines/install-relaypublisher.ps1"&lt;/span&gt;
                &lt;span class="s"&gt;if ($LASTEXITCODE -ne 0) {&lt;/span&gt;
                    &lt;span class="s"&gt;exit $LASTEXITCODE&lt;/span&gt;
                &lt;span class="s"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install latest relaypublisher from Azure Artifacts&lt;/span&gt;
              &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;SYSTEM_ACCESSTOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.AccessToken)&lt;/span&gt;
                &lt;span class="na"&gt;SYSTEM_COLLECTIONURI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.CollectionUri)&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;download&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current&lt;/span&gt;
              &lt;span class="na"&gt;artifact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manifest-list&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pwsh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;$ErrorActionPreference = 'Stop'&lt;/span&gt;
                &lt;span class="s"&gt;$manifestList = '$(Pipeline.Workspace)/manifest-list/manifest-list.json'&lt;/span&gt;
                &lt;span class="s"&gt;$outputDirectory = '$(Build.ArtifactStagingDirectory)/out'&lt;/span&gt;
                &lt;span class="s"&gt;New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null&lt;/span&gt;
                &lt;span class="s"&gt;$list = Get-Content -LiteralPath $manifestList -Raw | ConvertFrom-Json&lt;/span&gt;
                &lt;span class="s"&gt;if (@($list.manifests).Count -eq 0) {&lt;/span&gt;
                    &lt;span class="s"&gt;Write-Host 'No manifests to package; producing an empty package artifact.'&lt;/span&gt;
                    &lt;span class="s"&gt;exit 0&lt;/span&gt;
                &lt;span class="s"&gt;}&lt;/span&gt;

                &lt;span class="s"&gt;relaypublisher package `&lt;/span&gt;
                  &lt;span class="s"&gt;--repo-root '$(Build.SourcesDirectory)' `&lt;/span&gt;
                  &lt;span class="s"&gt;--manifest-list $manifestList `&lt;/span&gt;
                  &lt;span class="s"&gt;--output $outputDirectory&lt;/span&gt;
                &lt;span class="s"&gt;if ($LASTEXITCODE -ne 0) {&lt;/span&gt;
                    &lt;span class="s"&gt;exit $LASTEXITCODE&lt;/span&gt;
                &lt;span class="s"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Package installers&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.ArtifactStagingDirectory)/out&lt;/span&gt;
              &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish package artifact&lt;/span&gt;
              &lt;span class="na"&gt;artifact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;intune-packages&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to Intune&lt;/span&gt;
    &lt;span class="s"&gt;${{ if eq(parameters.sourceBuildId, '') }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;dependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Package&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;succeeded()&lt;/span&gt;
    &lt;span class="s"&gt;${{ else }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;dependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
    &lt;span class="na"&gt;lockBehavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sequential&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishToIntune&lt;/span&gt;
        &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish selected apps&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;intuneapps-production&lt;/span&gt;
        &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
        &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;runOnce&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;checkout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&lt;/span&gt;

                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${{ if eq(parameters.sourceBuildId, '') }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;download&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current&lt;/span&gt;
                    &lt;span class="na"&gt;artifact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manifest-list&lt;/span&gt;

                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;download&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current&lt;/span&gt;
                    &lt;span class="na"&gt;artifact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;intune-packages&lt;/span&gt;

                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${{ else }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DownloadPipelineArtifact@2&lt;/span&gt;
                    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Download manifest-list from build ${{ parameters.sourceBuildId }}&lt;/span&gt;
                    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                      &lt;span class="na"&gt;buildType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;specific&lt;/span&gt;
                      &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.TeamProject)&lt;/span&gt;
                      &lt;span class="na"&gt;definition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.DefinitionId)&lt;/span&gt;
                      &lt;span class="na"&gt;buildVersionToDownload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;specific&lt;/span&gt;
                      &lt;span class="na"&gt;pipelineId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ parameters.sourceBuildId }}&lt;/span&gt;
                      &lt;span class="na"&gt;artifactName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manifest-list&lt;/span&gt;
                      &lt;span class="na"&gt;targetPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Pipeline.Workspace)/manifest-list&lt;/span&gt;

                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DownloadPipelineArtifact@2&lt;/span&gt;
                    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Download intune-packages from build ${{ parameters.sourceBuildId }}&lt;/span&gt;
                    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                      &lt;span class="na"&gt;buildType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;specific&lt;/span&gt;
                      &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.TeamProject)&lt;/span&gt;
                      &lt;span class="na"&gt;definition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.DefinitionId)&lt;/span&gt;
                      &lt;span class="na"&gt;buildVersionToDownload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;specific&lt;/span&gt;
                      &lt;span class="na"&gt;pipelineId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ parameters.sourceBuildId }}&lt;/span&gt;
                      &lt;span class="na"&gt;artifactName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;intune-packages&lt;/span&gt;
                      &lt;span class="na"&gt;targetPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Pipeline.Workspace)/intune-packages&lt;/span&gt;

                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UseDotNet@2&lt;/span&gt;
                  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install .NET 10 SDK&lt;/span&gt;
                  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;packageType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sdk&lt;/span&gt;
                    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.x&lt;/span&gt;

                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NuGetAuthenticate@1&lt;/span&gt;
                  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authenticate Azure Artifacts&lt;/span&gt;

                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pwsh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                    &lt;span class="s"&gt;&amp;amp; "$(Build.SourcesDirectory)/.azurepipelines/install-relaypublisher.ps1"&lt;/span&gt;
                    &lt;span class="s"&gt;if ($LASTEXITCODE -ne 0) {&lt;/span&gt;
                        &lt;span class="s"&gt;exit $LASTEXITCODE&lt;/span&gt;
                    &lt;span class="s"&gt;}&lt;/span&gt;
                  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install latest relaypublisher from Azure Artifacts&lt;/span&gt;
                  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;SYSTEM_ACCESSTOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.AccessToken)&lt;/span&gt;
                    &lt;span class="na"&gt;SYSTEM_COLLECTIONURI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(System.CollectionUri)&lt;/span&gt;

                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AzureCLI@2&lt;/span&gt;
                  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to Intune&lt;/span&gt;
                  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;azureSubscription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(azureServiceConnection)&lt;/span&gt;
                    &lt;span class="na"&gt;scriptType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
                    &lt;span class="na"&gt;scriptLocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inlineScript&lt;/span&gt;
                    &lt;span class="na"&gt;inlineScript&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                      &lt;span class="s"&gt;set -euo pipefail&lt;/span&gt;
                      &lt;span class="s"&gt;manifest_list="$(Pipeline.Workspace)/manifest-list/manifest-list.json"&lt;/span&gt;
                      &lt;span class="s"&gt;package_dir="$(Pipeline.Workspace)/intune-packages"&lt;/span&gt;
                      &lt;span class="s"&gt;result_file="$(Build.ArtifactStagingDirectory)/publish-result.json"&lt;/span&gt;
                      &lt;span class="s"&gt;mkdir -p "$(Build.ArtifactStagingDirectory)"&lt;/span&gt;
                      &lt;span class="s"&gt;printf '[]\n' &amp;gt; "$result_file"&lt;/span&gt;

                      &lt;span class="s"&gt;manifest_count="$(jq -r '.manifests | length' "$manifest_list")"&lt;/span&gt;
                      &lt;span class="s"&gt;if [[ "$manifest_count" == '0' ]]; then&lt;/span&gt;
                        &lt;span class="s"&gt;echo 'No manifests to publish; producing an empty publish-result artifact.'&lt;/span&gt;
                        &lt;span class="s"&gt;exit 0&lt;/span&gt;
                      &lt;span class="s"&gt;fi&lt;/span&gt;

                      &lt;span class="s"&gt;if [[ -z "${EXPECTED_TENANT_ID:-}" || "$EXPECTED_TENANT_ID" == '$('* ]]; then&lt;/span&gt;
                        &lt;span class="s"&gt;echo 'EXPECTED_TENANT_ID must be configured as a protected pipeline variable.' &amp;gt;&amp;amp;2&lt;/span&gt;
                        &lt;span class="s"&gt;exit 1&lt;/span&gt;
                      &lt;span class="s"&gt;fi&lt;/span&gt;

                      &lt;span class="s"&gt;if [[ "$BUILD_SOURCEBRANCH" == 'refs/heads/main' &amp;amp;&amp;amp;&lt;/span&gt;
                            &lt;span class="s"&gt;"$BUILD_REASON" == 'IndividualCI' ]]; then&lt;/span&gt;
                        &lt;span class="s"&gt;dry_run=false&lt;/span&gt;
                      &lt;span class="s"&gt;elif [[ "$BUILD_SOURCEBRANCH" == 'refs/heads/main' &amp;amp;&amp;amp; "${ALLOW_MANUAL_PUBLISH:-False}" == 'True' ]]; then&lt;/span&gt;
                        &lt;span class="s"&gt;dry_run=false&lt;/span&gt;
                        &lt;span class="s"&gt;echo 'Manual run on main with allowManualPublish=true; publishing for real.'&lt;/span&gt;
                      &lt;span class="s"&gt;else&lt;/span&gt;
                        &lt;span class="s"&gt;dry_run=true&lt;/span&gt;
                        &lt;span class="s"&gt;echo 'This is not an automatic main CI build; running publish in dry-run mode.'&lt;/span&gt;
                      &lt;span class="s"&gt;fi&lt;/span&gt;

                      &lt;span class="s"&gt;export AZURE_TOKEN_CREDENTIALS=AzureCliCredential&lt;/span&gt;
                      &lt;span class="s"&gt;args=(publish&lt;/span&gt;
                        &lt;span class="s"&gt;--repo-root "$(Build.SourcesDirectory)"&lt;/span&gt;
                        &lt;span class="s"&gt;--manifest-list "$manifest_list"&lt;/span&gt;
                        &lt;span class="s"&gt;--package-dir "$package_dir"&lt;/span&gt;
                        &lt;span class="s"&gt;--expected-tenant "$EXPECTED_TENANT_ID"&lt;/span&gt;
                        &lt;span class="s"&gt;--source-commit "$BUILD_SOURCEVERSION"&lt;/span&gt;
                        &lt;span class="s"&gt;--result-file "$result_file")&lt;/span&gt;
                      &lt;span class="s"&gt;if [[ "$dry_run" == true ]]; then&lt;/span&gt;
                        &lt;span class="s"&gt;args+=(--dry-run)&lt;/span&gt;
                      &lt;span class="s"&gt;fi&lt;/span&gt;

                      &lt;span class="s"&gt;relaypublisher "${args[@]}"&lt;/span&gt;
                  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;ALLOW_MANUAL_PUBLISH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ parameters.allowManualPublish }}&lt;/span&gt;
                    &lt;span class="na"&gt;AZURE_TOKEN_CREDENTIALS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AzureCliCredential&lt;/span&gt;
                    &lt;span class="na"&gt;BUILD_REASON&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.Reason)&lt;/span&gt;
                    &lt;span class="na"&gt;BUILD_SOURCEBRANCH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.SourceBranch)&lt;/span&gt;
                    &lt;span class="na"&gt;BUILD_SOURCEVERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.SourceVersion)&lt;/span&gt;
                    &lt;span class="na"&gt;EXPECTED_TENANT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(EXPECTED_TENANT_ID)&lt;/span&gt;

                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.ArtifactStagingDirectory)/publish-result.json&lt;/span&gt;
                  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish result artifact&lt;/span&gt;
                  &lt;span class="na"&gt;artifact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish-result&lt;/span&gt;
                  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Publish only the changes&lt;/strong&gt;: &lt;code&gt;plan&lt;/code&gt; compares with the base ref and picks only the changed manifests. So even if I change several packages in separate commits, they are published together in one run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dry-run by default&lt;/strong&gt;: The pipeline publishes for real only on "automatic CI on main" or "a manual run with &lt;code&gt;allowManualPublish=true&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-run only Publish&lt;/strong&gt;: If you put the ID of an earlier run in &lt;code&gt;sourceBuildId&lt;/code&gt;, you can publish again without rebuilding the packages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approval gate&lt;/strong&gt;: If you add an approval check to &lt;code&gt;environment: intuneapps-production&lt;/code&gt;, someone can review before publishing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lockBehavior: sequential&lt;/code&gt;&lt;/strong&gt;: This stops two publish runs from running at the same time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Get relaypublisher inside the CI
&lt;/h2&gt;

&lt;p&gt;You can get relaypublisher in any of these ways. Pick the one that is easiest for you to run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub artifacts&lt;/li&gt;
&lt;li&gt;NuGet (mirroring it to an Azure Artifacts feed is handy)&lt;/li&gt;
&lt;li&gt;Homebrew&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the pipeline above, &lt;code&gt;install-relaypublisher.ps1&lt;/code&gt; installs the latest version from Azure Artifacts. Write this script to match the way you get the tool. (Please check the README for the package name and feed name.) I have also submitted it to winget, but it hasn't been accepted yet…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Caution&lt;/strong&gt;&lt;br&gt;
Creating intunewin files (&lt;code&gt;package&lt;/code&gt;) only works on Windows. That's why only the Package stage runs on &lt;code&gt;windows-latest&lt;/code&gt;. plan / validate / publish also work on Linux, so I use ubuntu for them because it's faster and cheaper.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Merge a PR and test it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a branch and add your manifest and scripts.&lt;/li&gt;
&lt;li&gt;Open a PR, and build validation runs. Publish runs in &lt;strong&gt;dry-run&lt;/strong&gt; mode, so nothing is registered in Intune. Check the logs to see which app will be assigned to which group.&lt;/li&gt;
&lt;li&gt;After the review, merge the PR.&lt;/li&gt;
&lt;li&gt;CI runs on main, and after approval, the app is published to Intune.&lt;/li&gt;
&lt;li&gt;In the Intune admin center, check the app and its assignments. Then make sure it installs on a test device.&lt;/li&gt;
&lt;li&gt;The publish result is saved in the &lt;code&gt;publish-result&lt;/code&gt; artifact, which is useful for tracking later.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The setup takes some time, but once it's done, registering apps in Intune becomes much easier. At home, I don't even want to manage my devices without it anymore. After the first setup, you can add apps just like you do with winget-pkgs. Please give it a try. I'm happy to get feedback on GitHub Issues.&lt;/p&gt;

</description>
      <category>intune</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>Codex for Visual Studio v0.4.0 / v0.4.1 Released</title>
      <dc:creator>KAMEGAWA Kazushi</dc:creator>
      <pubDate>Sat, 17 Oct 2026 10:09:28 +0000</pubDate>
      <link>https://dev.to/kkamegawa/codex-for-visual-studio-v040-v041-released-2f63</link>
      <guid>https://dev.to/kkamegawa/codex-for-visual-studio-v040-v041-released-2f63</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Translation note:&lt;/strong&gt; The original Japanese article was written by me. This English version was translated with the help of AI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've released v0.4.0 and v0.4.1 of &lt;strong&gt;Codex for Visual Studio&lt;/strong&gt;, a Visual Studio extension that lets you use Codex directly from Visual Studio.&lt;/p&gt;

&lt;p&gt;Version &lt;strong&gt;0.4.0&lt;/strong&gt; contains the main feature additions, while &lt;strong&gt;v0.4.1&lt;/strong&gt; includes dependency updates and smaller fixes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marketplace: &lt;a href="https://marketplace.visualstudio.com/items?itemName=KazushiKamegawa.relaytocodexforvs" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=KazushiKamegawa.relaytocodexforvs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;v0.4.0 Release Notes: &lt;a href="https://github.com/kkamegawa/vsextensionforcodex/releases/tag/v0.4.0" rel="noopener noreferrer"&gt;https://github.com/kkamegawa/vsextensionforcodex/releases/tag/v0.4.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;v0.4.1 Release Notes: &lt;a href="https://github.com/kkamegawa/vsextensionforcodex/releases/tag/v0.4.1" rel="noopener noreferrer"&gt;https://github.com/kkamegawa/vsextensionforcodex/releases/tag/v0.4.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main changes in this release are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Improved Goal / Plan mode behavior&lt;/li&gt;
&lt;li&gt;Remote Codex App Server support (Experimental)&lt;/li&gt;
&lt;li&gt;MXC sandbox support&lt;/li&gt;
&lt;li&gt;Updated for Codex CLI 0.162.1&lt;/li&gt;
&lt;li&gt;Other bug fixes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Improved Goal / Plan Mode Behavior
&lt;/h2&gt;

&lt;p&gt;Previously, when a goal started with &lt;code&gt;/goal&lt;/code&gt; was running, the button remained labeled &lt;strong&gt;Send&lt;/strong&gt;, making it impossible to stop execution.&lt;/p&gt;

&lt;p&gt;Starting with v0.4.0, the button changes to &lt;strong&gt;Stop&lt;/strong&gt; whenever a goal is active, including between turns during automatic continuation.&lt;/p&gt;

&lt;p&gt;When Stop is pressed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The goal is paused while preserving its objective, budget, and usage.&lt;/li&gt;
&lt;li&gt;The latest turn in the thread is interrupted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Timed-out operations are treated as having an unknown outcome and are &lt;strong&gt;not retried automatically&lt;/strong&gt;, preventing unexpected re-execution.&lt;/p&gt;

&lt;p&gt;Plan mode received several related improvements:&lt;/p&gt;

&lt;h3&gt;
  
  
  Persistent Plan Mode
&lt;/h3&gt;

&lt;p&gt;A checkbox has been added to the composer.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;/plan&lt;/code&gt; also enables it.&lt;/p&gt;

&lt;p&gt;Once enabled, Plan mode stays active until explicitly turned off, even if turns complete, fail, or are interrupted. If Codex asks follow-up questions, Plan mode remains active throughout the conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Codex-style Plan Presentation
&lt;/h3&gt;

&lt;p&gt;Suggested plans are displayed as Markdown under &lt;strong&gt;"Proposed plan"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a plan turn finishes, an &lt;strong&gt;"Implement this plan?"&lt;/strong&gt; card appears, allowing you to either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proceed with implementation&lt;/li&gt;
&lt;li&gt;Continue refining the plan&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Experimental API Detection
&lt;/h3&gt;

&lt;p&gt;Plan mode relies on the App Server's experimental &lt;code&gt;collaborationMode&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Because Codex rejects this field when Experimental APIs are unavailable, the extension now determines availability based on the currently negotiated connection rather than saved settings.&lt;/p&gt;

&lt;p&gt;When unsupported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Plan checkbox becomes disabled&lt;/li&gt;
&lt;li&gt;Entering &lt;code&gt;/plan&lt;/code&gt; explains how to enable the feature&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  One Question at a Time
&lt;/h3&gt;

&lt;p&gt;When multiple questions arrive, they are presented individually using a &lt;strong&gt;"Question n of N"&lt;/strong&gt; workflow.&lt;/p&gt;

&lt;p&gt;This also fixes earlier issues where answer choices could become partially hidden.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stop Works After Questions and Implementation
&lt;/h3&gt;

&lt;p&gt;Fixed an issue where the Stop button stopped working after:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting implementation from a plan&lt;/li&gt;
&lt;li&gt;Answering a question&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choices Embedded in Text Become Cards
&lt;/h3&gt;

&lt;p&gt;When Codex returns numbered or bulleted choices in normal text, those options are now converted into interactive question cards.&lt;/p&gt;

&lt;h3&gt;
  
  
  v0.4.1 Goal Confirmation Fix
&lt;/h3&gt;

&lt;p&gt;v0.4.1 fixes an issue where the final goal confirmation message failed to display the Yes/No card if the confirmation text was formatted in bold Markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Connect to a Remote Codex App Server (Experimental)
&lt;/h2&gt;

&lt;p&gt;The extension can now connect to an already-running Codex App Server through WebSocket.&lt;/p&gt;

&lt;p&gt;This feature is currently marked as &lt;strong&gt;Experimental&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The default behavior remains unchanged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The extension launches &lt;code&gt;codex app-server&lt;/code&gt; locally as a child process.&lt;/li&gt;
&lt;li&gt;Communication uses stdio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Internally, the extension still communicates with its local Worker. The Worker decides whether the backend transport should be stdio or WebSocket.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security and Connection Design
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Remote connections require &lt;code&gt;wss&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Plain &lt;code&gt;ws&lt;/code&gt; is allowed only for loopback (&lt;code&gt;localhost&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Certificate validation cannot be disabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authentication uses Bearer tokens.&lt;/p&gt;

&lt;p&gt;Profiles store only the path to the token file.&lt;/p&gt;

&lt;p&gt;The Worker reads the token during connection establishment, and the token itself is never persisted in configuration files or logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Path Mapping
&lt;/h3&gt;

&lt;p&gt;The extension maps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local solution root&lt;/li&gt;
&lt;li&gt;Remote server root&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Path matching is performed component-by-component rather than through string replacement.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will not match:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\repo2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation should also reject paths that escape through symlinks or junctions, although I have not yet fully validated those scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection Isolation
&lt;/h3&gt;

&lt;p&gt;Approvals, caches, and related state are isolated by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connection&lt;/li&gt;
&lt;li&gt;Account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents leakage between different server connections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reconnection Behavior
&lt;/h3&gt;

&lt;p&gt;When a connection drops:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic reconnection is attempted&lt;/li&gt;
&lt;li&gt;Draft messages remain in memory&lt;/li&gt;
&lt;li&gt;Operations with unknown delivery status are never retried automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Separate Commands
&lt;/h3&gt;

&lt;p&gt;Local connections use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Restart local app-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remote connections use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reconnect remote app-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The extension never attempts to stop a remote server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Guide
&lt;/h3&gt;

&lt;p&gt;I also added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;doc/codex-cli-setup.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;doc/codex-cli-setup_ja.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The guide covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI installation&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Configuration files&lt;/li&gt;
&lt;li&gt;Connection target settings&lt;/li&gt;
&lt;li&gt;Remote App Server setup&lt;/li&gt;
&lt;li&gt;Reconnection&lt;/li&gt;
&lt;li&gt;Diagnostics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far I've only tested loopback connections on the same machine.&lt;/p&gt;

&lt;p&gt;I haven't yet validated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-machine connections&lt;/li&gt;
&lt;li&gt;TLS deployments&lt;/li&gt;
&lt;li&gt;Cross-platform path translation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try those scenarios, I'd love to hear your feedback.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. MXC Sandbox Support
&lt;/h2&gt;

&lt;p&gt;For local App Server scenarios, you can now choose MXC as the Windows sandbox backend.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Windows sandbox&lt;/strong&gt; panel now provides three options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Follow Codex configuration&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Uses Codex configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prefer MXC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-c features.prefer_mxc=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses MXC when policy permits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Require MXC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-c windows.sandbox=mxc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Always uses MXC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Changes take effect the next time the local App Server starts.&lt;/p&gt;

&lt;p&gt;Remote profiles are unaffected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sandbox Status
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Check sandbox status&lt;/strong&gt; action shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Effective configuration&lt;/li&gt;
&lt;li&gt;Policy status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although it cannot determine which backend executed a specific command.&lt;/p&gt;

&lt;p&gt;MXC itself requires no setup.&lt;/p&gt;

&lt;p&gt;The elevated/unelevated setup options shown in the UI apply only to the traditional sandbox backends.&lt;/p&gt;

&lt;h3&gt;
  
  
  MXC Observation
&lt;/h3&gt;

&lt;p&gt;While testing, I noticed that force-terminating only the root Codex process under MXC may leave child processes running.&lt;/p&gt;

&lt;p&gt;This does not occur with the unelevated backend.&lt;/p&gt;

&lt;p&gt;Since the extension shuts down the entire process tree, users should not encounter this issue during normal use.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Updated for Codex CLI 0.162.1
&lt;/h2&gt;

&lt;p&gt;Communication with the App Server has been updated for compatibility with &lt;strong&gt;Codex CLI 0.162.1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because some remote servers may still use older versions, I continue to keep &lt;strong&gt;0.159.1&lt;/strong&gt; available for regression testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extension ↔ Worker Protocol Updates
&lt;/h3&gt;

&lt;p&gt;The protocol version between the extension and Worker has also been updated.&lt;/p&gt;

&lt;p&gt;Version 23 introduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Experimental API status reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;which enables the new Plan mode availability checks.&lt;/p&gt;

&lt;p&gt;MXC support adds protocol version 24.&lt;/p&gt;

&lt;p&gt;Unsupported combinations fail safely rather than attempting to continue with incompatible behavior.&lt;/p&gt;

&lt;p&gt;Additional 0.162.1-related changes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Goal operations now send &lt;code&gt;origin: "user"&lt;/code&gt; for proper authorization handling&lt;/li&gt;
&lt;li&gt;MCP OAuth completion is matched using &lt;code&gt;loginId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Skill paths are now treated as server-provided identifiers rather than requiring absolute paths&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Other Notable Fixes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fixed a banner that failed to disappear after successful manual reconnection&lt;/li&gt;
&lt;li&gt;Fixed a state where loading a remote token could leave the UI stuck at "Connecting / Checking account"&lt;/li&gt;
&lt;li&gt;Improved readability of question cards in dark themes by using Visual Studio theme resources&lt;/li&gt;
&lt;li&gt;Added an &lt;strong&gt;MCP details&lt;/strong&gt; toggle to collapse MCP server information&lt;/li&gt;
&lt;li&gt;Missing or unreadable attachments now cause send operations to fail instead of being silently skipped&lt;/li&gt;
&lt;li&gt;v0.4.1 updates NuGet dependencies, including MessagePack packages affected by a published vulnerability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;This release primarily focuses on improving Goal and Plan mode usability while laying the groundwork for remote connectivity.&lt;/p&gt;

&lt;p&gt;Remote App Server support is still experimental, so please file an issue if you encounter problems.&lt;/p&gt;

&lt;p&gt;To install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download &lt;code&gt;Codex.VisualStudio.Extension.vsix&lt;/code&gt; from GitHub Releases and double-click it, or&lt;/li&gt;
&lt;li&gt;Install directly from the Visual Studio Marketplace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Visual Studio 17.14 or later is required.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;After .NET 8 reaches end-of-support, I plan to fully migrate the extension to &lt;strong&gt;.NET 10&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At that point, &lt;strong&gt;Visual Studio 2026 or later&lt;/strong&gt; will be required.&lt;/p&gt;

</description>
      <category>visualstudio</category>
      <category>codex</category>
    </item>
    <item>
      <title>I Built RelayPublisher: A winget-like Publishing Workflow for Microsoft Intune</title>
      <dc:creator>KAMEGAWA Kazushi</dc:creator>
      <pubDate>Tue, 01 Sep 2026 05:09:19 +0000</pubDate>
      <link>https://dev.to/kkamegawa/i-built-relaypublisher-a-winget-like-publishing-workflow-for-microsoft-intune-1oep</link>
      <guid>https://dev.to/kkamegawa/i-built-relaypublisher-a-winget-like-publishing-workflow-for-microsoft-intune-1oep</guid>
      <description>&lt;blockquote&gt;
&lt;h2&gt;
  
  
  Note about this article
&lt;/h2&gt;

&lt;p&gt;The original version of this article was written entirely by me in Japanese.&lt;/p&gt;

&lt;p&gt;I used AI to translate and polish the English version for a global audience, but the ideas, design decisions, implementation details, and original content are all my own work.&lt;/p&gt;

&lt;p&gt;If you'd like to read the original Japanese article, you can find it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kkamegawa.hatenablog.jp/entry/2026/08/30/220813" rel="noopener noreferrer"&gt;https://kkamegawa.hatenablog.jp/entry/2026/08/30/220813&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Managing Intune Apps as Code with RelayPublisher
&lt;/h2&gt;

&lt;p&gt;Managing custom applications in Microsoft Intune has always felt more complicated than it should be.&lt;/p&gt;

&lt;p&gt;When you need to publish an internal application, you typically have a few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register it manually through the Intune portal&lt;/li&gt;
&lt;li&gt;Use one of the sample PowerShell scripts available online&lt;/li&gt;
&lt;li&gt;Build your own automation around Microsoft Graph APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these approaches work, but none of them feel particularly developer-friendly.&lt;/p&gt;

&lt;p&gt;In my case, the process was even more frustrating because I normally don't operate with elevated privileges. Every time I needed to publish or update an application, I had to perform a PIM elevation before making changes.&lt;/p&gt;

&lt;p&gt;That led me to a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why can't Intune application publishing work more like winget?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted a workflow where application definitions could live in source control, be described in YAML, and be published automatically through CI/CD.&lt;/p&gt;

&lt;p&gt;That idea became &lt;strong&gt;RelayPublisher&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kkamegawa" rel="noopener noreferrer"&gt;
        kkamegawa
      &lt;/a&gt; / &lt;a href="https://github.com/kkamegawa/Relaypublisher" rel="noopener noreferrer"&gt;
        Relaypublisher
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Relaypublisher&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Relaypublisher publishes winget-like YAML manifests as Microsoft Intune LOB apps from CI.&lt;/p&gt;
&lt;p&gt;Distribution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NuGet global tool package id: &lt;code&gt;relaypublisher&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Command name: &lt;code&gt;relaypublisher&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Package version: injected by CI from Git tag &lt;code&gt;vX.Y.Z&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Feeds: nuget.org, GitHub Packages (this repository), and Azure Artifacts&lt;/li&gt;
&lt;li&gt;Self-contained single-file apps for &lt;code&gt;win-x64&lt;/code&gt;, &lt;code&gt;win-arm64&lt;/code&gt;, and &lt;code&gt;osx-arm64&lt;/code&gt; are attached to each
GitHub release. They are neither code-signed nor notarized, so macOS shows a Gatekeeper warning.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Quick install:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;dotnet tool install --global relaypublisher&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;See &lt;a href="https://github.com/kkamegawa/Relaypublisher/doc/05-operation.md#0-tool-installation-and-version-control" rel="noopener noreferrer"&gt;doc/05-operation.md&lt;/a&gt; for installing
from GitHub Packages or Azure Artifacts instead.&lt;/p&gt;
&lt;p&gt;The repository now contains the .NET CLI foundation for the normal workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;validate&lt;/code&gt; checks manifest schema rules and repository-wide identity uniqueness.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plan&lt;/code&gt; resolves the target manifest set once and writes &lt;code&gt;manifest-list.json&lt;/code&gt; for later CI jobs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package&lt;/code&gt; stages app files: Windows Win32 &lt;code&gt;.intunewin&lt;/code&gt; packages (Windows runner required) or a
staged, checksum-verified macOS &lt;code&gt;.pkg&lt;/code&gt; (any OS).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;publish&lt;/code&gt; creates or updates Intune apps, uploads packaged content…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kkamegawa/Relaypublisher" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;/h2&gt;

&lt;h2&gt;
  
  
  What is RelayPublisher?
&lt;/h2&gt;

&lt;p&gt;RelayPublisher is an open-source tool that allows you to define Intune applications using YAML and publish them through automation.&lt;/p&gt;

&lt;p&gt;Instead of clicking through the Intune portal, you manage application definitions as code.&lt;/p&gt;

&lt;p&gt;If you're already familiar with Infrastructure as Code (IaC) or GitOps practices, the concept should feel natural.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;Historically, building this type of tool felt like a lot of work.&lt;/p&gt;

&lt;p&gt;There are many moving parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Graph integration&lt;/li&gt;
&lt;li&gt;Intune packaging&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Configuration management&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The emergence of generative AI significantly reduced the effort required to create and maintain a project like this. Tasks that would previously have been tedious became much easier to implement.&lt;/p&gt;

&lt;p&gt;As a result, I was finally able to build the tool I had wanted for a long time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;YAML-based application definitions&lt;/li&gt;
&lt;li&gt;CI/CD-friendly workflow&lt;/li&gt;
&lt;li&gt;Microsoft Intune integration&lt;/li&gt;
&lt;li&gt;Source-controlled application management&lt;/li&gt;
&lt;li&gt;Support for GitHub and Azure Repos workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A sample YAML definition is included in the repository to help you get started quickly.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;SchemaVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
&lt;span class="na"&gt;PackageIdentifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Microsoft.PowerShell&lt;/span&gt;
&lt;span class="na"&gt;PackageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell&lt;/span&gt;
&lt;span class="na"&gt;Developer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Microsoft Corporation&lt;/span&gt;
&lt;span class="na"&gt;Publisher&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Microsoft Corporation&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell 7 cross-platform automation and configuration tool.&lt;/span&gt;
&lt;span class="na"&gt;PackageVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;7.6.5&lt;/span&gt;
&lt;span class="na"&gt;AssignmentSync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;merge&lt;/span&gt;

&lt;span class="na"&gt;Apps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;macos&lt;/span&gt;
    &lt;span class="na"&gt;Architecture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arm64&lt;/span&gt;
    &lt;span class="na"&gt;InstallerType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pkg&lt;/span&gt;
    &lt;span class="c1"&gt;# pkg (default): unmanaged macOSPkgApp, unsigned allowed, up to 8 GB, and the&lt;/span&gt;
    &lt;span class="c1"&gt;# only AppType that can target MinimumOSVersion 14+ (v14_0/v15_0 are beta-only&lt;/span&gt;
    &lt;span class="c1"&gt;# flags; see doc/01-manifest-schema.md 5.7 and MacOsMinimumOperatingSystemTable).&lt;/span&gt;
    &lt;span class="na"&gt;AppType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pkg&lt;/span&gt;
    &lt;span class="na"&gt;DisplayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell [macOS ARM64]&lt;/span&gt;

    &lt;span class="c1"&gt;# Single Source item for macOS (unified source item shape, doc/01-manifest-schema.md 5.0.1).&lt;/span&gt;
    &lt;span class="c1"&gt;# PowerShell/PowerShell is a public repository, so Auth.Type: none is enough&lt;/span&gt;
    &lt;span class="c1"&gt;# for anonymous asset download.&lt;/span&gt;
    &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;githubRelease&lt;/span&gt;
      &lt;span class="na"&gt;Owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell&lt;/span&gt;
      &lt;span class="na"&gt;Repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PowerShell&lt;/span&gt;
      &lt;span class="na"&gt;Tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v7.6.5&lt;/span&gt;
      &lt;span class="na"&gt;AssetName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell-7.6.5-osx-arm64.pkg&lt;/span&gt;
      &lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell-7.6.5-osx-arm64.pkg&lt;/span&gt;
      &lt;span class="na"&gt;Sha256&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6ea58f4e91ab2df133ac18a42e291e4e870a623e3c5ab6f8368259cd9ac22770"&lt;/span&gt;
      &lt;span class="na"&gt;Auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;

    &lt;span class="na"&gt;Requirements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# PowerShell 7.6 (LTS) supports macOS 14 (Sonoma), 15 (Sequoia), and 26 (Tahoe).&lt;/span&gt;
      &lt;span class="c1"&gt;# 14.0 is the lowest version Relaypublisher's version table can map (26.0 is not&lt;/span&gt;
      &lt;span class="c1"&gt;# yet defined there); AppType: pkg allows the beta-only v14_0 flag.&lt;/span&gt;
      &lt;span class="na"&gt;MinimumOSVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;15.0"&lt;/span&gt;

    &lt;span class="na"&gt;Detection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;IgnoreAppVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;IncludedApps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;BundleId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.microsoft.powershell&lt;/span&gt;
          &lt;span class="na"&gt;BundleVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;7.6.5&lt;/span&gt;

    &lt;span class="c1"&gt;# Optional, AppType: pkg only (doc/01-manifest-schema.md §5.4.2): pre/post-install&lt;/span&gt;
    &lt;span class="c1"&gt;# shell scripts, mapped to Graph macOSPkgApp.preInstallScript/postInstallScript.&lt;/span&gt;
    &lt;span class="c1"&gt;# Paths are relative to --repo-root (samples/, per this file's own README).&lt;/span&gt;
    &lt;span class="na"&gt;Scripts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PreInstall&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scripts/macos/powershell/preinstall.sh&lt;/span&gt;
      &lt;span class="na"&gt;PostInstall&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scripts/macos/powershell/postinstall.sh&lt;/span&gt;

    &lt;span class="c1"&gt;# Left empty so this manifest applies unmodified in any tenant. Add your own&lt;/span&gt;
    &lt;span class="c1"&gt;# target group(s) before a real (non-dry-run) publish, for example:&lt;/span&gt;
    &lt;span class="c1"&gt;# Assignments:&lt;/span&gt;
    &lt;span class="c1"&gt;#   - Target: group&lt;/span&gt;
    &lt;span class="c1"&gt;#     GroupId: "&amp;lt;your-assignment-group-guid&amp;gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;#     Intent: required&lt;/span&gt;
    &lt;span class="na"&gt;Assignments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>opensource</category>
      <category>dotnet</category>
      <category>microsoft</category>
      <category>intune</category>
    </item>
    <item>
      <title>Visual Studio 2022 Memory Eater Extension</title>
      <dc:creator>KAMEGAWA Kazushi</dc:creator>
      <pubDate>Fri, 18 Jun 2021 11:19:02 +0000</pubDate>
      <link>https://dev.to/kkamegawa/visual-studio-2022-memory-eater-extension-4iem</link>
      <guid>https://dev.to/kkamegawa/visual-studio-2022-memory-eater-extension-4iem</guid>
      <description>&lt;p&gt;I had just published simple extension for Visual Studio 2022 Preview 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kkamegawa/memoryeaterextension"&gt;GitHub/kkamegawa/memoryeaterextension&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yC5_qQds--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aq1zophdaha135gvfq4y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yC5_qQds--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aq1zophdaha135gvfq4y.png" alt="Over 2GB use in Visual Studio"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This extension will not publish to marketplace. &lt;/p&gt;

&lt;p&gt;It's simple memory eater extension :-).&lt;/p&gt;

</description>
      <category>visualstudio</category>
    </item>
  </channel>
</rss>
