The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何正確設定 Azure Artifacts 的 NuGet 套件來源 (Feeds)

如果你在 Azure Artifacts 建立一個 NuGet 來源,由於所有存取 Azure Artifacts 上的 NuGet 套件都需要經過身分認證,如果你用的是 Visual Studio 就沒啥問題,更新到 Visual Studio 最新版就能搞定所有問題。但如果你用 Visual Studio Code 或 .NET CLI 進行 NuGet 操作,例如透過 nuget restoredotnet 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):

  1. 找到 %AppData%\NuGet\NuGet.Config 檔案,裡面會有個 <packageSourceCredentials> 可以看見加密過的認證資訊。

    你可以透過 nuget sources addnuget 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 建立

  2. 透過 NuGet 外掛 (plug-in) 的 認證提供者 (Credential Providers) 進行身分認證

    參見 Use NuGet with Azure DevOps Services feeds

    你在 Azure Artifacts 網頁介面中,會看到一個 Connect to feeds 功能,點下去之後會提供一個 Download NuGet + Credential Provider 按鈕 (如下圖示)。

    點擊該按鈕會下載一個 CredentialProviderBundle.zip 壓縮檔,請將裡面的 NuGet.exeCredentialProvider.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 restorenuget 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.exeCredentialProvider.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 的執行環境使用。

  3. 直接從命令列提示你輸入帳號密碼

    如果從 %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 來源至少需要一次手動輸入帳號密碼。

這裡有兩種不同的設定環境:

  1. 如果你是在 Azure Pipelines 持續建置 (CI) 平台中執行

    在 Azure Pipelines 環境中連到 Azure Artifacts 是不太需要設定的,預設就會用當下的使用者身分進行驗證。

  2. 如果你是在 非 Azure 環境 執行 (如 Jenkins CIGitLab CIDrone 等)

    使用 NuGet.exe 的做套件還原的話,請參考上述 NuGet 部分的「第一種」認證方式,透過 nuget sources 命令時,特別加上 -ConfigFile 指定 CI 過程中的 NuGet.config 設定檔即可。預設如我沒有指定 -ConfigFile 參數的話,會自動去抓 %AppData%\NuGet\NuGet.config 這個檔案。

    使用 dotnet 工具 (.NET CLI) 的話,就可以直接透過設定 環境變數 的方式達成,相對簡單很多!其設定步驟如下:

    1. 安裝 Azure Artifacts Credential Provider (本文上方有說明)

    2. 再到 https://dev.azure.com/{YourOrg}/_usersSettings/tokens 建立一個 Personal Access Tokens (PAT) 金鑰

      建立 PAT 時請記得設定適當的授權,盡量不要給 Full access 這麼大的權限!

    3. 最後設定幾個必要的 環境變數 即可

      請參考以下 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

  1. 初始化 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"
    
  2. 初始化團隊 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
    
  3. 調整 nuget.config 內容 (主要是 <packageSources> 套件來源設定)

    這套 Microsoft.VisualStudio.Services.NuGet.Bootstrap 工具預設會將 nuget.org 這個套件來源註解掉,因為一般來說企業內部只會開放允許的 NuGet 套件在企業內使用,不會開放所有 nuget.org 上面的公開套件。如果你需要開啟的話,請記得取消該行註解。

  4. packages.tools 資料夾加入 .gitignore (Git) 或 .tfignore (TFS) 忽略版控清單中,然後將新增與變更的檔案全部加入版控。

經過上述設定後,之後無論誰要加入專案團隊,第一次使用該專案時,只要先執行一次 .\init.psinit.cmd 即可初始化完整的 NuGet 相關設定,最新版的 NuGet 也會在執行過程中自動下載安裝。

這個 init 命令大概作了以下這些事:

  1. 自動下載最新的 NuGet.exeCredentialProvider.VSS.exe 工具,並置於 .\.tools 目錄下。
  2. 自動將 .\.tools\nuget.exe 加入目前工作階段的 PATH 環境變數,方便你執行 nuget 命令。

相關連結

留言評論