winget 的 Microsoft.Office 挑不了語言、通道與大量授權版本。改用 Office 部署工具,把 configuration.xml 用參數生出來。
發布於 2026-09-05・約 6 分鐘
新電腦交機流程裡,其他軟體一行 winget install 就結束,只有 Office 卡住。
winget install
winget install Microsoft.Office 裝得起來,但你不能挑語言、不能指定更新通道、不能決定要不要 Access 和 Publisher,大量授權版本(LTSC Volume)更是根本裝不了。它就是「預設的那一套」。企業環境幾乎沒有一台是預設的那一套。
winget install Microsoft.Office
正解是 Office 部署工具(Office Deployment Tool,ODT):
setup.exe /configure configuration.xml
麻煩在那份 XML。每個部署情境都要一份,手動維護一堆 XML 檔遲早會拿錯。我的做法是把 XML 用參數生出來,使用者在介面上選產品、通道、語言、排除哪些元件,程式現組。
到微軟下載中心(https://www.microsoft.com/download/details.aspx?id=49117)抓 Office 部署工具,那是一支自解壓縮 exe,執行後把解出來的 setup.exe 放進工具目錄底下的 Installers\ODT\。放好之後整包就可以直接複製到別台機器用。
https://www.microsoft.com/download/details.aspx?id=49117
setup.exe
Installers\ODT\
那個下載連結我刻意沒有寫死在程式裡。微軟每次改版網址都會變,寫死等於埋一個未來會壞的東西;改成一個可以填的欄位,填了就自動下載並用 /quiet /extract: 解開,沒填就報錯告訴你去哪裡抓。
/quiet /extract:
這是最容易錯的一格:
O365ProPlusRetail
MonthlyEnterprise
O365BusinessRetail
ProPlus2024Volume
PerpetualVL2024
ProPlus2021Volume
PerpetualVL2021
大量授權版本的通道要對到年份,選錯 ODT 會直接失敗。
Office 2013 / 2016 不能用 ODT。 這兩個版本的大量授權是 MSI 安裝版,ODT 只認 2019 以後。要裝就得改走離線安裝檔那條路:路徑指向 VLSC 下載出來的 setup.exe,靜默參數填 /config config.xml,XML 在安裝來源的 ProPlus.WW\config.xml 自己改。
/config config.xml
ProPlus.WW\config.xml
# 多語言要拆成多個 <Language>,不能塞成一個 ID="zh-tw,en-us" foreach ($lang in @($Language -split "[,;]" | ForEach-Object { $_.Trim() } | Where-Object { $_ })) { [void]$sb.AppendLine(" <Language ID=`"$(& $esc $lang)`" />") }
一、AUTOACTIVATE 只有大量授權版本吃。 零售或訂閱版填了會被忽略,所以我用產品代碼結尾判斷,*Volume 才寫進去。
AUTOACTIVATE
*Volume
二、有內網來源時同時帶 AllowCdnFallback:
AllowCdnFallback
<Add OfficeClientEdition="64" Channel="PerpetualVL2021" SourcePath="\\fileserver\office" AllowCdnFallback="True">
SourcePath 指到檔案伺服器,每台機器就不用各自從 CDN 抓三到四 GB。但檔案伺服器有一天會連不上,加了 AllowCdnFallback 才會自動退回 CDN,不會整批安裝失敗。
SourcePath
要先有內容才能指過去:找一台有網路的機器跑 setup.exe /download configuration.xml,把安裝來源抓進那個資料夾,之後每台機器讀它就好。
setup.exe /download configuration.xml
三、預設排除 Groove、Lync、Bing。 分別是舊版 OneDrive 同步工具、Skype for Business、搜尋擴充功能。不排除的話交機後使用者第一件事就是問這幾個是什麼。
Groove
Lync
Bing
設定檔是純文字,而且會跟著整包被複製到每一台機器。金鑰寫進去等於每台機器上都放了一份。
做法是設定檔裡只填環境變數的名稱,執行時才去讀值:
$pidKey = [Environment]::GetEnvironmentVariable($keyVar, "Machine") if (-not $pidKey) { $pidKey = [Environment]::GetEnvironmentVariable($keyVar, "Process") }
金鑰會被寫進暫存的 configuration.xml,所以那個檔案用完要立刻刪:
configuration.xml
try { Invoke-AsInstaller -Path $setup -Arguments "/configure `"$xmlPath`"" } finally { if ($hasPidKey -and (Test-Path $xmlPath)) { Remove-Item $xmlPath -Force } }
放在 finally 裡,安裝失敗拋例外時也會刪掉。另外,我平常會把生出來的 XML 內容印進記錄檔方便除錯,但帶了金鑰的那次不印。記錄檔會被留下來、會被貼進聊天室問問題。
finally
KMS 或帳號型啟用不需要填這個欄位,留空即可。
這是最後一個坑,也是我最晚才發現的。setup.exe 有時候會結束碼 0 但機器上根本沒有 Office。
所以裝完要回頭讀登錄檔確認:
$cfg = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration" -ErrorAction SilentlyContinue $ids = [string]$cfg.ProductReleaseIds
三種結果分開處理:
%TEMP%
原廠預裝的 Office 用 Click-to-Run 引擎移除:
OfficeClickToRun.exe scenario=install scenariosubtype=ARP sourcetype=None productstoremove=<ids> culture=zh-tw version.16=16.0 DisplayLevel=False
要注意這會移除機器上所有 Click-to-Run 版 Office,包含已經授權的版本。所以它跟「安裝 Office」基本上是一組,順序排好(先移除、後安裝),不要單獨勾移除那一項。
移除那一步如果逾時,記得砍掉行程再往下走。它會在背景繼續動,跟後面的安裝撞在一起,然後兩邊都失敗。
標籤:Office、ODT、Windows、部署、自動化