2020年12月30日 星期三

如何在 VM 的 Server Core 上啟用 WSL2

在Server Core 啟用WSL2

啟用VM的巢狀虛擬化

注意: Guest OS 的版本要在 Windows Server build 18945 版本以上,這裡的範例是使用 Windows Server 2004的版本

在VM安裝完成後,第一步就是先啟用VM的巢狀虛擬化

以Hyper-V為例(使用powershell)

Get-VM "VM的名字" | Set-VMProcessor -ExposeVirtualizationExtensions $true

各種VM啟用的方式,參考資料如下

Hyper-V 參考這裡

VMware 參考 這裡這裡

VirtualBox 參考這裡

接下來是進到VM裡安裝WSL2的步驟

1.啟用 Microsoft-Windows-Subsystem-Linux 及 VirtualMachinePlatform 這2個功能

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

安裝完之後重新開機

Restart-Computer

2.下載 Linux 核心更新套件

Invoke-WebRequest -Uri https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi -OutFile c:\wsl_update_x64.msi  -UseBasicParsing

下載完成後執行

c:\wsl_update_x64.msi

會有畫面出來讓你按下一步(我還以為Server Core都沒有UI咧)。
安裝完成之後重新開機

Restart-Computer

3.把 WSL 預設版本改成 WSL2

wsl --set-default-version  2

成功啟用會有下列訊息,正常只會出現一行 有關 WSL 2…
如果出現其他的表示沒有啟用成功

PS C:\> wsl --set-default-version 2
有關 WSL 2 的主要差異詳細資訊,請瀏覽 https://aka.ms/wsl2

4.到這裡 看要下載哪一個給WSL2 用的 Linux 發行版本

我是下載Ubuntu 20.04 這個版本

Invoke-WebRequest -Uri https://aka.ms/wslubuntu2004 -OutFile Ubuntu.zip -UseBasicParsing

5.下載完成之後解壓縮

Expand-Archive .\Ubuntu.zip .\Ubuntu

6.到解壓縮的目錄執行安裝檔 , 以Ubuntu 20.04為例是 ubuntu2004.exe

PS C:\> cd .\Ubuntu\
PS C:\Ubuntu> .\ubuntu2004.exe

7. 安裝完成之後需設定Linux 的帳號密碼

Please create a default UNIX user account. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: 
New password:
Retype new password:
passwd: password updated successfully

輸入exit 就會離開linux

8.檢查設定是否正常

PS C:\Ubuntu> wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Running         2

參考資料
1.Enable the Windows Subsystem for Linux
2.Windows 10 上適用於 Linux 的 Windows 子系統安裝指南

2019年8月4日 星期日

在微軟官方Asp.net Core runtime image裡安裝powershell

在微軟官方Asp.net Core runtime image裡安裝powershell

微軟官方 asp.net core 的image 其實是base 在 nano server上(https://github.com/dotnet/dotnet-docker/blob/master/2.2/runtime/nanoserver-1809/amd64/Dockerfile)

而nano server 為了縮小體積早就己經移除powershell。但是powershell是個好東西,沒有powershell對我們要做一些客製的行為很麻煩,所以才會想怎麼才能加回來。

其實也沒什麼密訣,現在微軟都把官方image的DockerFile 都放在Github上了,找到官方powershell image 的 Github,看一下人家怎麼做照做就是了

https://github.com/PowerShell/PowerShell-Docker/blob/master/release/stable/nanoserver/docker/Dockerfile

原理就是先找一個有powershell的image ,下載powershell core (註)裝起來後再Copy到 nano server 上

以下是我的DockerFile

# escape=`

FROM mcr.microsoft.com/windows/servercore:ltsc2019 AS installer-env

ARG PS_VERSION=6.2.0
ARG PS_PACKAGE_URL=https://github.com/PowerShell/PowerShell/releases/download/v${PS_VERSION}/PowerShell-${PS_VERSION}-win-x64.zip

SHELL ["C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", "-command"]

ARG PS_PACKAGE_URL_BASE64

RUN Write-host "Verifying valid Version..."; `
    if (!($env:PS_VERSION -match '^\d+\.\d+\.\d+(-\w+(\.\d+)?)?$' )) { `
        throw ('PS_Version ({0}) must match the regex "^\d+\.\d+\.\d+(-\w+(\.\d+)?)?$"' -f $env:PS_VERSION) `
    } `
    $ProgressPreference = 'SilentlyContinue'; `
    if($env:PS_PACKAGE_URL_BASE64){ `
        Write-host "decoding: $env:PS_PACKAGE_URL_BASE64" ;`
        $url = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($env:PS_PACKAGE_URL_BASE64)) `
    } else { `
        Write-host "using url: $env:PS_PACKAGE_URL" ;`
        $url = $env:PS_PACKAGE_URL `
    } `
    Write-host "downloading: $url"; `
    [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; `
    Invoke-WebRequest -Uri $url -outfile /powershell.zip -verbose; `
    Expand-Archive powershell.zip -DestinationPath  \PowerShell

# 裝到asp.net core 2.2 runtime 的 image上
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2     

# Copy PowerShell Core from the installer container
ENV ProgramFiles="C:\Program Files" `
    # set a fixed location for the Module analysis cache
    LOCALAPPDATA="C:\Users\ContainerAdministrator\AppData\Local" `
    PSModuleAnalysisCachePath="$LOCALAPPDATA\Microsoft\Windows\PowerShell\docker\ModuleAnalysisCache" `
    # Persist %PSCORE% ENV variable for user convenience
    PSCORE="$ProgramFiles\PowerShell\pwsh.exe"

# Copy PowerShell Core from the installer container
COPY --from=installer-env ["\\PowerShell\\", "$ProgramFiles\\PowerShell\\latest"]

# Set the path 這裡要注意一下,setx /M 需要管理者的權限,
# 但原本的身分是ContainerUser,所以需要先切換身分到ContainerAdministrator ,設完再切回來

USER ContainerAdministrator
RUN setx /M PATH "%ProgramFiles%\PowerShell\latest;%PATH%;"
USER ContainerUser

# intialize powershell module cache
RUN pwsh `
        -NoLogo `
        -NoProfile `
        -Command " `
          $stopTime = (get-date).AddMinutes(15); `
          $ErrorActionPreference = 'Stop' ; `
          $ProgressPreference = 'SilentlyContinue' ; `
          while(!(Test-Path -Path $env:PSModuleAnalysisCachePath)) {  `
            Write-Host "'Waiting for $env:PSModuleAnalysisCachePath'" ; `
            if((get-date) -gt $stopTime) { throw 'timout expired'} `
            Start-Sleep -Seconds 6 ; `
          }"

P.S. 新版的powershell ,己經可以跨平台了,並且改名叫 powershell core 了,而且執行檔名稱也改叫 pwsh.exe,不再是 powershell.exe

2019年7月25日 星期四

在Azure Devops Server 2019 的 powershell task 執行外部執行檔

在Azure Devops Server 2019 的 powershell task 執行外部執行檔

原本的需求是在 Pipelines 部署時,判斷如果docker-compose 己經有把container 建起來了,就更新container,否則就用docker-compose建立新的container。

但是在Pipelines的部署工作裡,似乎要做到上面的需求有困難。所以才想要在powershell裡執行docker-compose

底下是我預備要執行的powershell,

Set-Location -Path C:\composefile

$proxy = 'iisproxy'
$site1 = 'site-blue'
$site2 = 'site-green'

$cID = $(docker ps -qf "name=$proxy")

if(-NOT $cID){
 Write-Host "create new container"
 docker pull myhub.local/iisproxy
 docker pull myhub.local/app
        docker-compose up -d 
    }
    else {
        Write-Host "update container"
 docker pull myhub.local/app
        docker-compose stop $site1 
        docker-compose up -d --force-recreate --no-deps $site1  
 Start-Sleep -s 5
        docker-compose stop $site2
        docker-compose up -d --force-recreate --no-deps $site2        
    }

但是卻發現只要是執行到 docker-compose 就會出現 NativeCommandError 的錯誤

2019-07-24T08:00:10.7991464Z ##[error]位於 C:\azagent\A1\_work\_temp\46980d80-fab6-4a21-8fd7-1a6e7130d9a8.ps1:20 字元:9

+ docker-compose stop $site1  

2019-07-24T08:00:10.8097416Z ##[error]+ ~~~~~~~~~~~~~~~~~~~~~~~~~~  

2019-07-24T08:00:10.8204529Z ##[error] + CategoryInfo : NotSpecified: (Stopping site-blue ... :String) [], RemoteException  

2019-07-24T08:00:10.8301628Z ##[error] + FullyQualifiedErrorId : NativeCommandError  

2019-07-24T08:00:10.8403452Z ##[error]  

2019-07-24T08:00:10.8516019Z ##[error]PowerShell 已結束,代碼為 '1'。

但是直接開啟command視窗執行是正常的。 後來又發現在Powershell ISE 裡執行指令就會有一樣的錯誤。 原來兇手是 powershell 自己,害我錯怪 docker-compose 好幾天。

自此,才有比較明確的追查方向。
找到在stackoverflow上一樣症頭的討論

https://stackoverflow.com/questions/2095088/error-when-calling-3rd-party-executable-from-powershell-when-using-an-ide/20950421#20950421

原來是因為 powershell 在處理 STDERR (standard error) 有不同的行為才造成這樣的錯

照文章內的好幾個做法,要在 Pipelines裡可以正常執行要改成這樣
用 2>&1 來吃掉多出來的錯誤

&cmd /c "docker-compose up -d 2>&1"

下面是完整的script

Set-Location -Path C:\composefile

$proxy = 'iisproxy'
$site1 = 'site-blue'
$site2 = 'site-green'

$cID = $(docker ps -qf "name=$proxy")

if(-NOT $cID){
 Write-Host "create new container"
 docker pull myhub.local/iisproxy
 docker pull myhub.local/app
        &cmd /c "docker-compose up -d 2>&1"
    }
    else {
        Write-Host "update container"
 docker pull myhub.local/app
        &cmd /c "docker-compose stop $site1 2>&1"
        &cmd /c "docker-compose up -d --force-recreate --no-deps $site1 2>&1"
 Start-Sleep -s 5
        &cmd /c "docker-compose stop $site2 2>&1"
        &cmd /c "docker-compose up -d --force-recreate --no-deps $site2 2>&1"
    }

在winodws container 使用 chrome headless 做 angular unit test

在winodws container 使用 chrome headless 做 angular unit test

現在 azure pipeline 來到前端的unit test 階段
想像中應該就像在自己電腦裡執行一樣,如果可以正常用chrome headless 執行 unit test 放到docker 應該不會有問題。BUT 事情總是沒有這麼單純。

以下是我部份的karma.config.js 跟DockerFile

module.exports = function (config) {
  config.set({
    customLaunchers: {
      ChromeHeadlessDocker: {
        base: 'ChromeHeadless',
        flags: [
          '--disable-web-security',
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage',
          '--remote-debugging-port=9223',
          '--headless',
          '--disable-gpu'
        ]
      }
    },
    .
    .
    .
  });
};

DockerFile

RUN ng test --watch=false --code-coverage --browsers=ChromeHeadlessDocker --reporters=junit,progress

結果一直會出現

ChromeHeadless have not captured in 60000 ms, killing.

這才想到…我沒有裝chrome在docker container裡面

原本我是用

RUN Invoke-WebRequest -OutFile ChromeStandaloneSetup64.exe https://dl.google.com/tag/s/installdataindex/update2/installers/ChromeStandaloneSetup64.exe;
RUN ./ChromeStandaloneSetup64.exe /silent /install

但是卻一直裝不成功。( 如果有裝成功會在 C:\Program Files (x86)\ 底下有個Google 的目錄)
後來到 https://cloud.google.com/chrome-enterprise/browser/download/ 下載MSI的版本,先COPY到 image裡面再下指令裝

COPY ./GoogleChromeStandaloneEnterprise64.msi /
RUN Start-Process -FilePath msiexec -ArgumentList /q, /i, GoogleChromeStandaloneEnterprise64.msi -Wait ; `
Remove-Item -Force GoogleChromeStandaloneEnterprise64.msi

確認chrome有裝起來之後,再執行docker build 還是出現

ChromeHeadless have not captured in 60000 ms, killing.

把 karma.config.js 裡的 logLevel: config.LOG_INFO, 改成 config.LOG_DEBUG 也沒得到什麼有用的資訊

這個時候己經快瘋了,在這裡卡關卡了三天,無論改config或者一些奇奇怪怪的方法都沒效。

而且重點是網路上找到的方法,其實都是Linux container的解決方案。 Windows Container 根本一整個弱掉了。

後來終於在這裡找到 https://stackoverflow.com/questions/52948655/chrome-on-windows-docker-container-is-not-working 原來是缺少字型的問題. 一整個 WHAT THE ~~~

這位好心的 Prom3theu5 還提供了字型及安裝方法
https://github.com/prom3theu5/ServerCoreFonts

照著上面的方法,安裝字型之後終於可以看到開始測試的訊息了。

2019-07-08T00:39:39.6996247Z  [32m08 07 2019 08:39:39.675:INFO [karma-server]:  [39mKarma v3.1.1 server started at http://0.0.0.0:9876/

2019-07-08T00:39:39.6999650Z  [32m08 07 2019 08:39:39.698:INFO [launcher]:  [39mLaunching browsers ChromeHeadlessCI with concurrency unlimited

2019-07-08T00:39:39.7124483Z  [32m08 07 2019 08:39:39.710:INFO [launcher]:  [39mStarting browser ChromeHeadless

2019-07-08T00:40:35.5387718Z  [32m08 07 2019 08:40:35.537:INFO [HeadlessChrome 0.0.0 (Windows 10 0.0.0)]:  [39mConnected on socket Gsf6g9YFWi0lxoFVAAAA with id 57991930

2019-07-08T00:40:49.6356010Z HeadlessChrome 0.0.0 (Windows 10 0.0.0): Executed 0 of 439 SUCCESS (0 secs / 0 secs)

2019-07-08T00:40:49.7481854Z  [1A [2KHeadlessChrome 0.0.0 (Windows 10 0.0.0): Executed 1 of 439 SUCCESS (0 secs / 0.079 secs)

2019年7月10日 星期三

在Azure Pipeline 裡使用 docker build 執行 asp.net core 的 unit test 後,把測試結果跟code coverage 上傳到組建裡

在Azure Pipeline 裡使用 docker build 執行 asp.net core 的 unit test 後,把測試結果跟code coverage 上傳到組建裡

在Pipeline建置時,如果要看到測試結果跟code coverage , 需要在Pipeline中建置,才能在azure devops server 的介面中看到測試結果跟程式碼含蓋範圍。像這樣
enter image description here

但是如果要把unit test 放在 docker 裡,在 multi stage builds 的過程之中執行unit test,在預設的狀況下,測試結果會在某一個build stage中,就不能使用pipeline的task來上傳測試結果。

解決方法

在docker 建置過程中做unit test , 另外產生測試結果。等到docker build 結束之後,用powershell把測試結果copy出來,然後再分別用 pipeline 的task 上傳測試結果及程式碼含蓋範圍。 就像下圖的步驟 3,4,5一樣。

enter image description here

Build docker image

因為我有好幾個測試專案,預設會產生好幾個code coverage的report 。 所以在執行dotnet test 有一些特殊的參數來讓這些report 合併成一個。

第一個是 CoverletOutputFormat=“json%2ccobertura” ,用來指定產出report的格式,注意這個 %2c 是分號 ; 的意思。 但是如果直接寫;在docker build 時會有powershell的錯誤。弄了很久才找到解法,改成%2C (是ascii code ; 的意思)就可以過了。

第二個是MergeWith=/testresults/coverage/coverage.json 。用MergeWith 來指定多個 code coverage 的report 跟誰合併,要指定report的位置在個目錄。

另外,在dockerfile 裡我有下了一個 label 叫 test=true ,這個是用來讓docker build 完之後還可以找到暫存的stage ,從裡面把我們的測試報告copy出來。

底下是我部份的dockerfile , 我的web.sln 裡面有3個程式專案及4個測試專案。

RUN dotnet restore web.sln
RUN dotnet build web.csproj -c Release -o /app/out 

# unit test
#先下一個label 
LABEL test=true
# 安裝dotnet-reportgenerator-globaltool 用來產生cobertura 格式的 code coverage report
RUN dotnet tool install dotnet-reportgenerator-globaltool --version 4.2.2 --tool-path /tools  

# %2c 是 ;(分號) 很重要!! 很重要!! 因為直接用; 會出錯。
RUN dotnet test --results-directory /testresults --logger:trx /p:CollectCoverage=true /p:CoverletOutputFormat="json%2ccobertura" /p:MergeWith=/testresults/coverage/coverage.json /p:Exclude="[xunit.*]*" /p:CoverletOutput=/testresults/coverage/ web.sln

RUN /tools/reportgenerator.exe "-reports:/testresults/coverage/coverage.cobertura.xml"  "-targetdir:/testresults/coverage/reports"  "-reporttypes:HTMLInline`;HTMLChart"

用powershell 取得測試結果

enter image description here

script說明

  1. 先找到下label=test的 docker id, 預設會新的在最上面,所以抓第 0 個
  2. 用找到的id 建立一個臨時的container
  3. 從container把測試結果copy 出來
  4. 這個臨時的container就可以移除惹
$id=$($(docker images --filter "label=test=true" -q)[0]);
docker create --name testcontainer $id;
docker cp testcontainer:/testresults ./;
docker rm testcontainer;

發行測試結果

enter image description here
測試結果格式選 VSTest
搜尋資料夾要選對,我的例子是 testresults

發行程式碼涵蓋範圍結果

enter image description here
程式碼涵蓋範圍工具 選cobertura
摘要檔案 跟 報告目錄 路徑要設對


如果都成功了,就可以在組建的摘要裡看到測試結果了,就像第一張圖一樣。 也可以在測試的tab看到詳細的結果
enter image description here

同場加映

看完上面的,有沒有感到奇怪,為什麼沒有code coverage 的詳細資料。
因為這是 Azure Devops Server 2019 目前版本的bug,但是線上版的Azure devops 是有詳細資料的。

在 2019/5/22 己經有人回報給 visual studio team了 ,MS也有回覆下個release會修好。

Code coverage tab missing in Azure DevOps Server

另外

前端(angular) 的unit test 也可以用類似的方法,產出測試報告跟上傳到devops server上,有空再來分享另一段血淚史。

2019年6月20日 星期四

Azure Devops Server 2019 更改電腦名稱

Azure Devops Server 2019 更改電腦名稱

在新VM裡安裝 Azure Devops Server 2019 時,沒有注意電腦名稱是什麼,後來想要改電腦名稱時,也沒想那麼多就直接改了。結果,改完重開機就連不上web 介面了,一直轉圈圈。
心裡想一定有什麼跟電腦名稱綁在一起無法執行。 後來在

C:\Program Files\Azure DevOps Server 2019\Application Tier\Web Services\web.config

找到資料庫連線字串的設定是用電腦名稱

    <add key="applicationDatabase" value="Data Source=WIN-IL123HEV1A2;Initial Catalog=AzureDevOps_Configuration;Integrated Security=True;Encrypt=False" />

把 Data Source 換成新的電腦名稱再重新開機一次就都正常了。

2019年6月18日 星期二

Azure Pipelines 建立部署群組卡關

Azure Pipelines 建立部署群組卡關

現在在測試新的Devpos環境, 以現有使用TFS的經驗,Azure Devops Server 2019 自然是第一個考慮的工具。

把Azure Devops Server 2019 的安裝比較沒什麼問題,基本上安裝程式會檢查環境,有缺什麼會顯示出來。例如:要先安裝SQL SERVER;要有JDK(沒有的話,打個勾勾會自動裝OpenJDK) 。 這個比遙遠的當年 TFS 2005 裝了好幾天才裝起來好太多了 。

不過安裝好之後,要在pipelines建第一個部署群組就卡關了,"部署群組"就是要部署到哪裡去,被部署的SERVER上面要裝一個代理程式( agent) 。

But… 安裝agent,居然是叫你自己下powershell command的方式。虧我前面還在誇Azure Devops Server 2019的安裝介面做的不錯。

雖然在介面上他己經產生好script,把他貼到powershell裡面去執行就好了。看起來一塊蛋榚啊!! 介面長的像這樣
部署群組圖1

照畫面上的指示,把那一堆Script放到powershell的command裡執行
結果
部署群組圖二

哇哩,反覆的仔細檢查script.

才發現179字元 “Administrator” 有問題, 把 “ ” 換成正常的 "

enter image description here

再來一次

PS C:\> $ErrorActionPreference="Stop";If(-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole( [Security.Principal.WindowsBuiltInRole] "Administrator")){ throw "在系統管理員 PowerShell 提示字元處執行命令"};If($PSVersionTable.PSVersion -lt (New-Object System.Version("3.0"))){ throw "指令碼 (3.0) 需要的 Windows PowerShell 最低版本,與目前正在執行的 Windows PowerShell 版本不符。" };If(-NOT (Test-Path $env:SystemDrive\'azagent')){mkdir $env:SystemDrive\'azagent'}; cd $env:SystemDrive\'azagent'; for($i=1; $i -lt 100; $i++){$destFolder="A"+$i.ToString();if(-NOT (Test-Path ($destFolder))){mkdir $destFolder;cd $destFolder;break;}}; $agentZip="$PWD\agent.zip";$DefaultProxy=[System.Net.WebRequest]::DefaultWebProxy;$securityProtocol=@();$securityProtocol+=[Net.ServicePointManager]::SecurityProtocol;$securityProtocol+=[Net.SecurityProtocolType]::Tls12;[Net.ServicePointManager]::SecurityProtocol=$securityProtocol;$WebClient=New-Object Net.WebClient; $Uri='https://go.microsoft.com/fwlink/?linkid=2066756';if($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))){$WebClient.Proxy= New-Object Net.WebProxy($DefaultProxy.GetProxy($Uri).OriginalString, $True);}; $WebClient.DownloadFile($Uri, $agentZip);Add-Type -AssemblyName System.IO.Compression.FileSystem;[System.IO.Compression.ZipFile]::ExtractToDirectory( $agentZip, "$PWD");.\config.cmd --deploymentgroup --deploymentgroupname "first" --agent $env:COMPUTERNAME --runasservice --work '_work' --url 'http://172.16.1.111/' --collectionname 'DefaultCollection' --projectname 'uuuu' --auth Integrated; Remove-Item $agentZip;


    目錄: C:\azagent


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        2019/6/17  下午 03:18                A1


>> 連線:

正在連線到伺服器...

>> 註冊代理程式:

正在掃描工具功能。
正在連接至伺服器。
請輸入 代理程式的部署群組標記? (Y/N) (請為 否 按 Enter) >
已成功新增代理程式
正在測試代理程式連線。
2019-06-17 07:22:07Z: 已儲存設定。
請輸入 要用於服務的使用者帳戶 (請為 NT AUTHORITY\SYSTEM 按 Enter) >
正在將檔案權限授與 'NT AUTHORITY\SYSTEM'。
已成功安裝服務 vstsagent.172.TEST
服務 vstsagent.172.TEST 已成功設定復原選項
服務 vstsagent.172.TEST 已成功設定為延遲自動啟動
已成功設定服務 vstsagent.172.TEST
已成功啟動服務 vstsagent.172.TEST

終於可以註冊上去了,握拳!!


同場加映:如何移除 agent

從上面的script可以發現,他其實只做2件事,

  1. 下載1個zip 檔,然後解壓縮到C:\azagent\A1
  2. 執行裡面的config.cmd 跟著一堆參數

所以要移除agent , 一樣用有管理員身分的powershell到C:\azagent\A1 裡執行 config.cmd 只是參數換成remove

PS C:\azagent\a1> .\config.cmd remove
正在移除服務
正在等候服務結束...
成功:正在移除服務
正在從伺服器移除代理程式
請輸入 驗證類型 (請為 Integrated 按 Enter) >
正在連線到伺服器...
成功:正在從伺服器移除代理程式
正在移除 .credentials
成功:正在移除 .credentials
正在移除 .agent
成功:正在移除 .agent