如果你在 Azure Artifacts 建立一個 NuGet 來源,由於所有存取 Azure Artifacts 上的 NuGet 套件都需要經過身分認證,如果你用的是 Visual Studio 就沒啥問題,更新到 Visual Studio 最新版就能搞定所有問題。但如果你用 Visual Studio Code 或 .NET CLI 進行 NuGet 操作,例如透過 nuget restore
或 dotnet add package
等命令,就會開始出現問題。這篇文章將說明如何正確設定,讓日後的 NuGet 操作都不再出問題。
重現問題
我會發現這個問題是因為我在用 Visual Studio Code 寫 .NET Core 應用程式,很多時候我都習慣透過 dotnet add package 來新增套件,而這次新增的套件是放在 Azure Artifacts 提供的 NuGet feeds 裡。
接著我先在專案根目錄 (方案目錄) 建立一個 NuGet.config
設定檔
dotnet new nuget
然後在命令提示字元執行以下命令,加入 Azure Artifacts 的 NuGet 來源網址 (Feeds)
nuget sources add -Name {YourOrg} -Source https://{YourOrg}.pkgs.visualstudio.com/_packaging/{YourProject}/nuget/v3/index.json
如果要使用 NuGet
命令列工具,你可以透過 Chocolatey 安裝:choco install nuget.commandline
然後很簡單的加入一個 Microsoft.Extensions.Logging
套件,問題就發生了!
dotnet add package Microsoft.Extensions.Logging
這時畫面上就會顯示以下錯誤,很明顯是權限不足造成 ( 401 (Unauthorized)
):
error: 無法從遠端來源 '{URL}' 擷取 'Microsoft.Extensions.Logging.Debug' 相關資訊。
error: Response status code does not indicate success: 401 (Unauthorized).
使用 dotnet 存取 Azure Artifacts 套件來源(Feeds)
如果要使用 .NET CLI
命令列工具 (dotnet
),你必須下載 Azure Artifacts Credential Provider 才能順利通過 Azure Artifacts 的身分驗證。
以下是 PowerShell 自動安裝命令,安裝過程會建立 %USERPROFILE%\.nuget\plugins
目錄,並將支援 .NET CLI
的 認證提供者 (Credential Providers) 安裝進去:
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1'))
另一種安裝方式為:
wget https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1 -OutFile installcredprovider.ps1
.\installcredprovider.ps1
如果要更新現有版本,可以執行以下命令:
.\installcredprovider.ps1 -Force
這套工具還有一個 netfx
版本,官方建議不要手動安裝,因為這個版本會隨著 Visual Studio 自動更新,你安裝了之後,Visual Studio 就不會自動更新了。所以如果你要在 CI 伺服器上安裝,而該電腦又沒有安裝 Visual Studio 的話,可以執行以下命令:
.\installcredprovider.ps1 -AddNetfx
如果是 Linux 平台,可以執行以下命令,過程中也會完全自動安裝 認證提供者 到 $HOME/.nuget/plugins/
目錄下:
curl -s -L https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh | bash
在安裝好 認證提供者 之後,通常要先執行一次身分認證,之後才可以免輸入密碼。你可以執行一次 dotnet restore --interactive
命令,這裡的 --interactive
參數會讓你在執行的時候提示你如何通過身分驗證,也唯有這樣才能正確使用。
dotnet restore --interactive
第一次身分驗證通過後,身分認證就會記憶起來,預設會儲存在 Session Token Cache Locations 中,路徑如下:
- Windows (cmd):
%LOCALAPPDATA%\MicrosoftCredentialProvider
- Windows (PS):
$env:UserProfile\AppData\Local\MicrosoftCredentialProvider
- Linux/MAC:
$HOME/.local/share/MicrosoftCredentialProvider/
如果你日後想忘記密碼,可以到上述目錄刪除 SessionTokenCache.dat
檔案即可。
使用 NuGet 存取 Azure Artifacts 套件來源(Feeds)
原則上 NuGet
命令列工具會用以下順序檢查是否有合法的認證資訊(Credentials):
-
找到 %AppData%\NuGet\NuGet.Config
檔案,裡面會有個 <packageSourceCredentials> 可以看見加密過的認證資訊。
你可以透過 nuget sources add
或 nuget sources update
命令,來更新 Feeds 的預設登入帳號密碼。範例如下:
nuget sources add -Name Duotify -Source https://{YourOrg}.pkgs.visualstudio.com/_packaging/{YourProject}/nuget/v3/index.json -UserName optional -PassWord [PAT]
請注意:PAT (Personal Access Tokens) 可以到 https://dev.azure.com/{YourOrg}/_usersSettings/tokens
建立
-
透過 NuGet 外掛 (plug-in) 的 認證提供者 (Credential Providers) 進行身分認證
參見 Use NuGet with Azure DevOps Services feeds
你在 Azure Artifacts 網頁介面中,會看到一個 Connect to feeds 功能,點下去之後會提供一個 Download NuGet + Credential Provider
按鈕 (如下圖示)。
點擊該按鈕會下載一個 CredentialProviderBundle.zip
壓縮檔,請將裡面的 NuGet.exe
與 CredentialProvider.VSS.exe
執行檔複製到你的專案內,直接跟 NuGet.config
設定檔放一起即可。
注意:這個 CredentialProviderBundle.zip
壓縮檔,可以直接透過以下網址結構取得,不用登入也可以獲取。
https://pkgs.dev.azure.com/{YourOrg}/_apis/public/nuget/client/CredentialProviderBundle.zip
記得要將 {YourOrg}
換成你公司的 Azure DevOps 組織名稱,例如 miniasp.visualstudio.com
就會是 miniasp
這個名字。
接著你就可以嘗試透過 nuget restore
或 nuget list
命令,測試是不是能夠連到 Azure Artifacts feeds 進行驗證,這個 CredentialProvider.VSS.exe
工具會自動被啟動,跳出 Azure 的登入視窗,成功登入之後就會自動記憶密碼,下次就不用再輸入密碼驗證。
注意:你只要成功登入一遍,就再也不能換身分,目前我還找不到怎樣才能忘記之前透過認證快取暫存的登入資料。我已經到 Stack Overflow 的這裡發問,過幾天可以看看有沒有解答。
透過 CredentialProvider.VSS.exe
工具進行登入時,開啟的視窗是用萬惡的 Internet Explorer 瀏覽器,如果你的電腦安裝的是 Windows Server 的話,很有可能會因為 IE 的預設安全性過高,導致完全無法執行 JavaScript,連帶的也會讓你無法透過 CredentialProvider.VSS.exe
登入成功!如果你真的遇到這種狀況,可能就要換其他方式通過認證。不過,我個人花了不少時間實驗解決方案,發現這問題只要安裝 netfx
版本的 Azure Artifacts Credential Provider 就能解決問題,這個版本提供更完善的登入機制。
不過,我個人通常不喜歡把 NuGet 放進專案中,而是習慣透過 Chocolatey 直接安裝 NuGet.exe 與 CredentialProvider.VSS.exe 套件,所有設定都會自動配置完成:
choco install nuget.commandline nuget-credentialprovider-vss -y
當然,你也可以手動將 CredentialProvider.VSS.exe
複製到以下路徑 (這是 NuGet 認證提供者外掛的預設全域路徑),未來在這台電腦上所有的 NuGet.exe
都會自動找到這個 認證提供者 (Credential Providers)。但還是透過上述的 choco install nuget-credentialprovider-vss
命令安裝比較快!
- Windows (cmd):
%LOCALAPPDATA%\NuGet\CredentialProviders
- Windows (PS):
$env:UserProfile\AppData\Local\NuGet\CredentialProviders
除了全域設定外,你也可以透過設定 NUGET_CREDENTIALPROVIDERS_PATH
環境變數,來指定 認證提供者 (Credential Providers) 外掛所在路徑,這個設定很適合用在需要 CI/CD 的執行環境使用。
-
直接從命令列提示你輸入帳號密碼
如果從 %AppData%\NuGet\NuGet.Config
檔案,或是從 NuGet 外掛 (plug-in) 的 認證提供者 (Credential Providers) 都找不到認證資訊,預設就會有互動介面幫你跟 Azure Artifacts feeds 進行驗證,缺點是每次都要你輸入帳號密碼進行驗證。
請注意:上述 3 點僅適用於 NuGet.exe
命令,並不適用於 dotnet restore
命令!
以下有兩個重要連結可以參考:
使用 MSBuild 還原 Azure Artifacts 套件
在以上動作都設定好之後,若要透過 MSBuild 執行 NuGet 套件還原工作,第一次使用要先指定 /p:nugetInteractive=true
屬性來執行,之後就會自動通過驗證。
msbuild /t:restore /p:nugetInteractive=true
透過 CI/CD 自動化建置部署的注意事項
透過 CI/CD 通常意味著必須在無互動模式下自動化執行,此時上述步驟就不適用,因為透過上述步驟連到 Azure Artifacts 的 NuGet 來源至少需要一次手動輸入帳號密碼。
這裡有兩種不同的設定環境:
-
如果你是在 Azure Pipelines 持續建置 (CI) 平台中執行
在 Azure Pipelines 環境中連到 Azure Artifacts 是不太需要設定的,預設就會用當下的使用者身分進行驗證。
-
如果你是在 非 Azure 環境 執行 (如 Jenkins CI、GitLab CI 或 Drone 等)
使用 NuGet.exe
的做套件還原的話,請參考上述 NuGet 部分的「第一種」認證方式,透過 nuget sources
命令時,特別加上 -ConfigFile
指定 CI 過程中的 NuGet.config
設定檔即可。預設如我沒有指定 -ConfigFile
參數的話,會自動去抓 %AppData%\NuGet\NuGet.config
這個檔案。
使用 dotnet
工具 (.NET CLI) 的話,就可以直接透過設定 環境變數 的方式達成,相對簡單很多!其設定步驟如下:
-
安裝 Azure Artifacts Credential Provider (本文上方有說明)
-
再到 https://dev.azure.com/{YourOrg}/_usersSettings/tokens
建立一個 Personal Access Tokens (PAT) 金鑰
建立 PAT 時請記得設定適當的授權,盡量不要給 Full access
這麼大的權限!
-
最後設定幾個必要的 環境變數 即可
請參考以下 JSON 內容,將 Feeds 網址放入 endpoint 屬性,將產生好的 PAT 放入 password 屬性,最後將這份 JSON 字串設定給 VSS_NUGET_EXTERNAL_FEED_ENDPOINTS
環境變數,之後的 CI/CD 在執行 dotnet restore
的過程就會順利通過身分驗證,完全不需要互動介面操作。
{"endpointCredentials": [{"endpoint":"https://{YourOrg}.pkgs.visualstudio.com/_packaging/{YourProject}/nuget/v3/index.json", "password":"[PAT]"}]}
如何替團隊設定共用的 NuGet 環境設定 (加入版控)
Set up a multi-developer NuGet environment | Microsoft Docs
-
初始化 Git 本地儲存庫 (Optional)
若你開發的專案是 .NET 相關的話,建議你可以建立一個預設的 .gitignore
檔案。你可以嘗試使用 PowerShell 執行以下命令,下載 .gitignore
的初始內容。
Invoke-WebRequest https://www.gitignore.io/api/visualstudio -OutFile .gitignore
接著初始化 Git 本地儲存庫 (非必要步驟):
git init
git add .
git commit -m "Initial commit"
-
初始化團隊 NuGet 設定
Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile nuget.exe
.\nuget.exe install -OutputDirectory packages Microsoft.VisualStudio.Services.NuGet.Bootstrap
.\packages\Microsoft.VisualStudio.Services.NuGet.Bootstrap.*\tools\Bootstrap.ps1
rm .\nuget.exe
echo ".tools/" | Out-File -Encoding ascii -Append -FilePath .\.gitignore
-
調整 nuget.config
內容 (主要是 <packageSources>
套件來源設定)
這套 Microsoft.VisualStudio.Services.NuGet.Bootstrap
工具預設會將 nuget.org
這個套件來源註解掉,因為一般來說企業內部只會開放允許的 NuGet 套件在企業內使用,不會開放所有 nuget.org
上面的公開套件。如果你需要開啟的話,請記得取消該行註解。
-
將 packages
與 .tools
資料夾加入 .gitignore
(Git) 或 .tfignore
(TFS) 忽略版控清單中,然後將新增與變更的檔案全部加入版控。
經過上述設定後,之後無論誰要加入專案團隊,第一次使用該專案時,只要先執行一次 .\init.ps
或 init.cmd
即可初始化完整的 NuGet 相關設定,最新版的 NuGet 也會在執行過程中自動下載安裝。
這個 init
命令大概作了以下這些事:
- 自動下載最新的 NuGet.exe 與 CredentialProvider.VSS.exe 工具,並置於
.\.tools
目錄下。
- 自動將
.\.tools\nuget.exe
加入目前工作階段的 PATH
環境變數,方便你執行 nuget
命令。
相關連結
- Azure DevOps
- NuGet
- Stack Overflow