Windows 的自訂指令碼延伸模組

「自訂指令碼延伸模組」會在 Azure 虛擬機器 (VM) 上下載並執行指令碼。 使用此延伸模組對部署後設定、軟體安裝或其他任何設定或管理工作。 您可以從 Azure 儲存體或 GitHub 下載指令碼,或是在擴充功能執行階段將指令碼提供給 Azure 入口網站。

自訂指令碼功能可以與 Azure Resource Manager 範本整合。 您也可以使用 Azure CLI、Azure PowerShell、Azure 入口網站或 Azure 虛擬機器 REST API 來執行。

本文說明如何使用 Azure PowerShell 模組和 Azure Resource Manager 範本,來使用自訂指令碼延伸模組。 也提供 Windows 系統的疑難排解步驟。

必要條件

注意

請勿使用自訂指令碼延伸模組以相同的 VM 執行 Update-AzVM 作為其參數。 延伸模組會等候其本身。

支援的 Windows 作業系統

Windows OS x64
Windows 10 支援
Windows 11 支援
Windows Server 2008 SP2 支援
Windows Server 2008 R2 支援
Windows Server 2012 支援
Windows Server 2012 R2 支援
Windows Server 2016 支援
Windows Server 2016 Core 支援
Windows Server 2019 支援
Windows Server 2019 Core 支援
Windows Server 2022 支援
Windows Server 2022 Core 支援

指令碼位置

您可以將延伸模組設定為使用 Azure Blob 儲存體認證,以便於存取 Azure Blob 儲存體。 例如,只要 VM 可以路由至端點、GitHub 或內部檔案伺服器,指令碼位置可以位於任一處。

網際網路連線能力

若要從外部下載指令碼,例如 GitHub 或 Azure 儲存體,您需要開啟其他防火牆或網路安全性群組 (NSG) 連接埠。 例如,如果您的指令碼位於 Azure 儲存體,您可以使用適用於儲存體的 Azure NSG 服務標籤允許存取

自訂指令碼延伸模組沒有任何方法可略過憑證驗證。 如果您要從安全位置下載 (例如自我簽署憑證),您可能會收到像是根據驗證程序,遠端憑證無效的錯誤。 請確定憑證已正確安裝在 VM 上的信任的根憑證授權單位存放區中。

如果您的指令碼是在本機伺服器上,仍可能需要開啟其他防火牆或 NSG 連接埠。

提示

  • 輸出僅限於最後 4,096 個位元組。
  • 正確逸出字元有助於確保正確剖析字串。 例如,處理檔案路徑時,您一律需要兩個反斜線來逸出單一常值反斜線。 範例:{"commandToExecute": "C:\\Windows\\System32\\systeminfo.exe >> D:\\test.txt"}
  • 此擴充功能的最高失敗率導因於指令碼中的語法錯誤。 確認指令碼執行無誤。 將更多記錄放入指令碼,以便於更容易找到失敗。
  • 撰寫具有等冪性的指令碼,因此即使意外執行多次,也不會造成系統變更。
  • 請確定在指令碼執行時,不需要使用者輸入。
  • 指令碼可執行 90 分鐘。 一旦超過此時間,就會導致延伸模組佈建失敗。
  • 請勿將重新啟動置於指令碼內。 此動作會導致正在安裝的延伸模組發生問題,而且延伸模組也不會在重新啟動後繼續。
  • 如果您有指令碼在安裝應用程式和執行指令碼前就導致重新啟動,則請使用 Windows 排程工作或如 DSC、Chef 或 Puppet 延伸模組等工具來排程重新啟動。
  • 請勿執行會導致 VM agent 停止或更新的指令碼。 它可能會讓延伸模組處於轉換狀態,並導致逾時。
  • 延伸模組只會執行一次指令碼。 如果您想要在每次啟動時執行指令碼,請使用延伸模組建立 Windows 排程工作。
  • 如果您想要排程指令碼的執行時間,請使用延伸模組建立 Windows 排程工作。
  • 當指令碼正在執行時,只會在 Azure 入口網站或 CLI 中看到正在轉換延伸模組狀態。 如果您需要執行中指令碼更頻繁的狀態更新,便必須建立自己的解決方案。
  • 自訂指令碼延伸模組原本不支援 Proxy 伺服器。 不過,您可以在指令碼中使用支援 Proxy 伺服器的檔案傳輸工具,例如 Invoke-WebRequest
  • 請留意指令碼或命令可能依賴的非預設目錄位置。 有邏輯可以處理此情況。
  • 自訂指令碼延伸模組會在 LocalSystem 帳戶下執行。
  • 如果您打算使用 storageAccountNamestorageAccountKey 屬性,這些屬性必須在 protectedSettings 中收集。
  • 您只能將一個版本的延伸模組套用至 VM。 若要執行第二個自訂指令碼,您可以使用新的設定更新現有延伸模組。 或者,您可以移除自訂指令碼延伸模組,並重新使用更新的指令碼套用。

擴充功能結構描述

「自訂指令碼擴充功能」組態會指定指令碼位置和要執行命令等項目。 您可將此組態儲存在組態檔中、在命令列中指定組態,或在 Azure Resource Manager 範本中指定組態。

您可將敏感性資料儲存在受保護的組態中,此組態會經過加密,並且只會在 VM 內解密。 當執行命令包含密碼或共用存取簽章 (SAS) 檔案參考等祕密時,受保護的組態很有用。 以下是範例:

{
    "apiVersion": "2018-06-01",
    "type": "Microsoft.Compute/virtualMachines/extensions",
    "name": "virtualMachineName/config-app",
    "location": "[resourceGroup().location]",
    "dependsOn": [
        "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),copyindex())]",
        "[variables('musicstoresqlName')]"
    ],
    "tags": {
        "displayName": "config-app"
    },
    "properties": {
        "publisher": "Microsoft.Compute",
        "type": "CustomScriptExtension",
        "typeHandlerVersion": "1.10",
        "autoUpgradeMinorVersion": true,
        "settings": {
            "timestamp":123456789
        },
        "protectedSettings": {
            "commandToExecute": "myExecutionCommand",
            "storageAccountName": "myStorageAccountName",
            "storageAccountKey": "myStorageAccountKey",
            "managedIdentity" : {},
            "fileUris": [
                "script location"
            ]
        }
    }
}

注意

managedIdentity 屬性「不得」storageAccountNamestorageAccountKey 屬性搭配使用。

VM 上一次只能安裝一個擴充功能版本。 在相同 Azure Resource Manager 範本中針對相同 VM 指定自訂指令碼兩次將會失敗。

您可以在 VM 資源內使用此結構描述,也可以將此結構描述作為獨立資源。 如果將此擴充功能作為 Azure Resource Manager 範本中的標準資源,則資源名稱的格式必須是 virtualMachineName/extensionName

屬性值

名稱 值或範例 資料類型
apiVersion 2015-06-15 date
publisher Microsoft.Compute string
type CustomScriptExtension string
typeHandlerVersion 1.10 int
fileUris https://raw.githubusercontent.com/Microsoft/dotnet-core-sample-templates/master/dotnet-core-music-windows/scripts/configure-music-app.ps1 陣列
timestamp 123456789 32 位元整數
commandToExecute powershell -ExecutionPolicy Unrestricted -File configure-music-app.ps1 string
storageAccountName examplestorageacct string
storageAccountKey TmJK/1N3AbAZ3q/+hOXoi/l73zOqsaxXDhqa9Y83/v5UpXQp2DQIBuv2Tifp60cE/OaHsJZmQZ7teQfczQj8hg== string
managedIdentity { }{ "clientId": "31b403aa-c364-4240-a7ff-d85fb6cd7232" }{ "objectId": "12dd289c-0583-46e5-b9b4-115d5c19ef4b" } JSON 物件

注意

這些屬性名稱會區分大小寫。 為了避免發生部署問題,請使用如下所示的名稱。

屬性值詳細資料

屬性 選擇性或必要 詳細資料
fileUris 選擇性 要下載的 URL 或檔案。 如果 URL 是敏感性的,例如包含金鑰,則這個欄位應該在 protectedSettings 中指定。
commandToExecute 必要 要執行的進入點指令碼。 如果您的命令包含祕密 (例如密碼),或您的檔案 URI 是敏感資料,請改用此屬性。
timestamp 選擇性 只在觸發重新執行指令碼的情況下,變更此值。 只要與先前的值不同,任何整數值都可接受。
storageAccountName 選擇性 儲存體帳戶的名稱。 如果您指定儲存體認證,則所有 fileUris 值都必須是 Azure Blob 的 URL。
storageAccountKey 選擇性 儲存體帳戶的存取金鑰。
managedIdentity 選擇性 用於下載檔案的受控識別。 有效值為 clientId (選用、字串),這是受控識別的用戶端識別碼,而 objectId (選用、字串),這是受控識別的物件識別碼。

公開設定會以純文字格式,傳送到執行指令碼所在的 VM。 受保護的設定會透過只有 Azure 和 VM 知道的金鑰加密。 設定會以傳送時的形式,儲存至 VM。 也就是說,如果設定經加密,會以加密形式儲存在 VM 上。 用來解密加密值的憑證會儲存在 VM 上。 此憑證也會用來在執行階段解密設定,如有需要。

使用公開設定可能有助於偵錯,但是建議您使用受保護的設定。

您可以在公用或受保護的設定中設定下列值。 延伸模組會拒絕任何在公用和受保護的設定中設定下列值的設定。

  • commandToExecute
  • fileUris

屬性:managedIdentity

注意

必須在受保護的設定中,才能指定此屬性。

自訂指令碼延伸模組,1.10 版和更新版本支援受控識別,從 fileUris 設定中提供的 URL 下載檔案。 屬性可讓自訂指令碼延伸模組存取 Azure 儲存體私人 Blob 或容器,而不需要使用者傳遞 SAS 權杖或儲存體帳戶金鑰之類的祕密。

若要使用這項功能,將系統指派使用者指派的身分識別,新增至預期執行自訂指令碼延伸模組的 VM 或虛擬機器擴展集。 然後將受控識別存取權授與 Azure 儲存體容器或 Blob

若要在目標 VM 或虛擬機器擴展集上,使用系統指派的身分識別,請將 managedidentity 設定為空白 JSON 物件。

{
  "fileUris": ["https://mystorage.blob.core.windows.net/privatecontainer/script1.ps1"],
  "commandToExecute": "powershell.exe script1.ps1",
  "managedIdentity" : {}
}

若要在目標 VM 或虛擬機器擴展集上,使用使用者指派的身分識別,請使用受控識別的用戶端識別碼或物件識別碼設定 managedidentity

{
  "fileUris": ["https://mystorage.blob.core.windows.net/privatecontainer/script1.ps1"],
  "commandToExecute": "powershell.exe script1.ps1",
  "managedIdentity" : { "clientId": "31b403aa-c364-4240-a7ff-d85fb6cd7232" }
}
{
  "fileUris": ["https://mystorage.blob.core.windows.net/privatecontainer/script1.ps1"],
  "commandToExecute": "powershell.exe script1.ps1",
  "managedIdentity" : { "objectId": "12dd289c-0583-46e5-b9b4-115d5c19ef4b" }
}

注意

managedIdentity 屬性「不得」storageAccountNamestorageAccountKey 屬性搭配使用。

範本部署

您可以使用 Azure Resource Manager 範本部署 Azure VM 延伸模組。 上一節詳述的 JSON 結構描述可以用於 Azure Resource Manager 範本,在部署範本期間執行自訂指令碼延伸模組。 下列範例會說明如何使用「自訂指令碼延伸模組」:

PowerShell 部署

您可以使用 Set-AzVMCustomScriptExtension 命令,將「自訂指令碼延伸模組」新增至現有的虛擬機器。 如需詳細資訊,請參閱 Set-AzVMCustomScriptExtension

Set-AzVMCustomScriptExtension -ResourceGroupName <resourceGroupName> `
    -VMName <vmName> `
    -Location myLocation `
    -FileUri <fileUrl> `
    -Run 'myScript.ps1' `
    -Name DemoScriptExtension

範例

使用多個指令碼

在此範例中,使用三個指令碼來建置伺服器。 commandToExecute 屬性會呼叫第一個指令碼。 接著您可以選擇其他指令碼的呼叫方式。 例如,您可以擁有負責控制執行的前置指令碼,其中包含正確的錯誤處理、記錄和狀態管理。 指令碼會下載到本機電腦執行。

例如,在 1_Add_Tools.ps1 中,您會藉由將 .\2_Add_Features.ps1 新增至指令碼來呼叫 2_Add_Features.ps1。 針對在 $settings 中定義的其他指令碼重複這個程序。

$fileUri = @("https://xxxxxxx.blob.core.windows.net/buildServer1/1_Add_Tools.ps1",
"https://xxxxxxx.blob.core.windows.net/buildServer1/2_Add_Features.ps1",
"https://xxxxxxx.blob.core.windows.net/buildServer1/3_CompleteInstall.ps1")

$settings = @{"fileUris" = $fileUri};

$storageAcctName = "xxxxxxx"
$storageKey = "1234ABCD"
$protectedSettings = @{"storageAccountName" = $storageAcctName; "storageAccountKey" = $storageKey; "commandToExecute" = "powershell -ExecutionPolicy Unrestricted -File 1_Add_Tools.ps1"};

#run command
Set-AzVMExtension -ResourceGroupName <resourceGroupName> `
    -Location <locationName> `
    -VMName <vmName> `
    -Name "buildserver1" `
    -Publisher "Microsoft.Compute" `
    -ExtensionType "CustomScriptExtension" `
    -TypeHandlerVersion "1.10" `
    -Settings $settings `
    -ProtectedSettings $protectedSettings;

從本機共用執行指令碼

在此範例中,您可以使用本機伺服器訊息區 (SMB) 伺服器作為指令碼的位置。 您就不需要提供任何其他設定,但 commandToExecute 除外。

$protectedSettings = @{"commandToExecute" = "powershell -ExecutionPolicy Unrestricted -File \\filesvr\build\serverUpdate1.ps1"};

Set-AzVMExtension -ResourceGroupName <resourceGroupName> `
    -Location <locationName> `
    -VMName <vmName> `
    -Name "serverUpdate"
    -Publisher "Microsoft.Compute" `
    -ExtensionType "CustomScriptExtension" `
    -TypeHandlerVersion "1.10" `
    -ProtectedSettings $protectedSettings

使用 CLI 多次執行自訂指令碼

如果已傳遞完全相同的設定,自訂指令碼延伸模組處理常式將會防止重新執行指令碼。 此行為可防止意外重新執行,如果指令碼不具等冪性,可能會導致非預期的行為。 若要確認處理程式是否封鎖重新執行,請檢視 C:\WindowsAzure\Logs\Plugins\Microsoft.Compute.CustomScriptExtension\<HandlerVersion>\CustomScriptHandler.log*。 搜尋如下的警告:

Current sequence number, <SequenceNumber>, is not greater than the sequence number
of the most recently executed configuration. Exiting...

如果您想要執行「自訂指令碼延伸模組」多次,您只有在下列情況下才能這麼做:

  • 延伸模組的 Name 參數等同於先前的延伸模組部署。
  • 您已更新組態。 您可以將動態屬性新增至命令,例如時間戳記。 如果處理常式偵測到組態設定中的變更,會將該變更視為重新執行指令碼的明確需求。

或者,您也可以將 ForceUpdateTag 屬性設定為 true

使用 Invoke-WebRequest

如果您在指令碼中使用 Invoke-WebRequest,您必須指定參數 -UseBasicParsing。 如果您未指定參數,則會在檢查詳細狀態時收到下列錯誤:

The response content cannot be parsed because the Internet Explorer engine
is not available, or Internet Explorer's first-launch configuration
is not complete. Specify the UseBasicParsing parameter and try again.

虛擬機器擴展集

如果您從 Azure 入口網站部署自訂指令碼延伸模組,就無法控制存取儲存體帳戶中指令碼之 SAS 權杖的到期日。 初始部署可運作,但儲存體帳戶的 SAS 權杖到期時,任何後續擴充作業都失敗,因為自訂指令碼延伸模組無法再存取儲存體帳戶。

在虛擬機器擴展集上部署自訂指令碼延伸模組時,建議您使用 PowerShellAzure CLI 或 Azure Resource Manager 範本。 如此一來,您可以選擇使用受控識別,或直接控制 SAS 權杖的到期日,以便只要您需要,即可存取儲存體帳戶中的指令碼。

疑難排解與支援

您可以從 Azure 入口網站擷取關於延伸模組部署狀態的資料,以及使用 Azure PowerShell 模組來擷取。 若要查看 VM 的延伸模組部署狀態,請執行下列命令:

Get-AzVMExtension -ResourceGroupName <resourceGroupName> `
    -VMName <vmName> -Name myExtensionName

延伸模組輸出會記錄至目標虛擬機器上下列資料夾中的檔案:

C:\WindowsAzure\Logs\Plugins\Microsoft.Compute.CustomScriptExtension

指定檔案會下載至目標虛擬機器上的下列資料夾之中:

C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.*\Downloads\<n>

在上述路徑中,<n> 是可能會在延伸模組執行之間變更的十進位整數。 1.* 值符合擴充功能目前實際的 typeHandlerVersion 值。 例如,實際的目錄可能是 C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.8\Downloads\2

執行 commandToExecute 命令時,延伸模組會將此目錄,例如 ...\Downloads\2,設定為目前的工作目錄。 此程序可讓您使用相對路徑找出使用 fileURIs 屬性下載的檔案位置。 以下是下載檔案的範例:

fileUris 中的 URI 相對下載位置 絕對下載位置
https://someAcct.blob.core.windows.net/aContainer/scripts/myscript.ps1 ./scripts/myscript.ps1 C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.8\Downloads\2\scripts\myscript.ps1
https://someAcct.blob.core.windows.net/aContainer/topLevel.ps1 ./topLevel.ps1 C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.8\Downloads\2\topLevel.ps1

如同上述,絕對目錄路徑會隨 VM 生命週期而變更,但不會在自訂指令碼延伸模組的單次執行期間。

由於絕對下載路徑會隨時間而改變,最好盡可能在 commandToExecute 字串中選擇相對的指令碼/檔案路徑。 例如:

"commandToExecute": "powershell.exe . . . -File \"./scripts/myscript.ps1\""

會為使用 fileUris 屬性清單下載的檔案保留第一個 URI 區段後的路徑資訊。 如稍早的資料表中所示,下載的檔案會對應到下載的子目錄,以反映 fileUris 值結構。

支援