Extensión de la secuencia de comandos personalizada para Windows

La extensión de script personalizado descarga y ejecuta scripts en máquinas virtuales (VM) de Azure. Use esta extensión para la configuración posterior a la implementación, la instalación de software o cualquier otra tarea de configuración o administración. Puede descargar scripts de Azure Storage o GitHub, o bien proporcionárselos a Azure Portal en tiempo de ejecución de la extensión.

La extensión de script personalizado se integra con las plantillas de Azure Resource Manager. También puede ejecutarla mediante la CLI de Azure, Azure PowerShell, Azure Portal o la API de REST de Azure Virtual Machines.

En este artículo se describe cómo usar la extensión de script personalizado mediante el módulo de Azure PowerShell y las plantillas de Azure Resource Manager. También se proporcionan pasos para solucionar problemas de sistemas Windows.

Requisitos previos

Nota:

No use la extensión de script personalizado para ejecutar Update-AzVM con la misma máquina virtual como su parámetro. La extensión esperará a sí misma.

Sistemas operativos Windows compatibles

SO Windows x64
Windows 10 Compatible
Windows 11 Compatible
Windows Server 2008 SP2 Compatible
Windows Server 2008 R2 Compatible
Windows Server 2012 Compatible
Windows Server 2012 R2 Compatible
Windows Server 2016 Compatible
Windows Server 2016 Core Compatible
Windows Server 2019 Compatible
Windows Server 2019 Core Compatible
Windows Server 2022 Compatible
Windows Server 2022 Core Compatible

Ubicación de los scripts

Puede establecer la extensión de modo que use las credenciales de Azure Blob Storage a fin de acceder a Azure Blob Storage. La ubicación del script puede ser cualquier lugar, siempre y cuando la máquina virtual pueda enrutarse a ese punto de conexión, por ejemplo, GitHub o un servidor de archivos interno.

Conectividad de Internet

Para descargar un script externamente, por ejemplo, desde GitHub o Azure Storage, debe abrir puertos adicionales de firewall o de grupo de seguridad de red (NSG). Por ejemplo, si el script se encuentra en Azure Storage, puede permitir el acceso mediante etiquetas de servicio NSG de Azure para Storage.

La extensión de script personalizado no tiene ninguna manera de omitir la validación de certificados. Por lo tanto, si descarga desde una ubicación segura, por ejemplo, un certificado autofirmado, puede que obtenga errores como El certificado remoto no es válido según el procedimiento de validación. Asegúrese de que el certificado esté instalado correctamente en el almacén Entidades de certificación raíz de confianza de la máquina virtual.

Si el script está en un servidor local, es posible que tenga que abrir otros puertos de firewall o NSG.

Sugerencias

  • La salida se limita a los últimos 4096 bytes.
  • Los caracteres de escape correctos ayudarán a garantizar que las cadenas se analizan correctamente. Por ejemplo, siempre necesita dos barras diagonales inversas para escapar una sola barra diagonal inversa literal al tratar con rutas de acceso de archivo. Ejemplo: {"commandToExecute": "C:\\Windows\\System32\\systeminfo.exe >> D:\\test.txt"}
  • La mayor tasa de errores para esta extensión se debe a errores de sintaxis en el script. Compruebe que el script se ejecuta sin errores. Coloque otro registro en el script para facilitar la búsqueda de errores.
  • Escriba scripts que sean idempotentes, para que si se ejecutan más de una vez accidentalmente no se produzcan cambios en el sistema.
  • Asegúrese de que los scripts no requieran la intervención del usuario cuando se ejecutan.
  • El script puede ejecutarse durante 90 minutos. Un valor superior a este provoca un error de aprovisionamiento de la extensión.
  • No coloque reinicios dentro del script. Esta acción provoca problemas con otras extensiones que se estén instalando y la extensión no continúa después del reinicio.
  • Si tiene un script que provoca un reinicio, antes de instalar aplicaciones y ejecutar scripts, programe el reinicio con una tarea programada de Windows o con herramientas como las extensiones DSC, Chef o Puppet.
  • No ejecute un script que provoque la detención o actualización del agente de máquina virtual. Esto puede dejar la extensión en un estado de transición y provocar tiempo de espera.
  • La extensión ejecuta un script solo una vez. Si quiere ejecutar un script en cada inicio, use la extensión para crear una tarea programada de Windows.
  • Si quiere programar cuándo se va a ejecutar un script, use la extensión para crear una tarea programada de Windows.
  • Cuando el script se esté ejecutando, solo verá un estado de extensión en transición desde Azure Portal o la CLI de Azure. Si quiere recibir actualizaciones de estado más frecuentes de un script en ejecución, cree su propia solución.
  • La extensión de script personalizado no admite servidores proxy de forma nativa. Aun así, puede usar una herramienta de transferencia de archivos que admita servidores proxy dentro del script, como Invoke-WebRequest.
  • Tenga en cuenta las ubicaciones de directorio no predeterminadas en las que se puedan basar los scripts o comandos. Aplique lógica para controlarlas.
  • Asegúrese de que no tiene ninguna configuración personalizada en la clave del Registro HKLM\SOFTWARE\Microsoft\Command Processor\AutoRun (detallada aquí). Esto se desencadenaría durante la instalación o habilitación de la extensión de script personalizado y provocaría un error como 'XYZ is not recognized as an internal or external command, operable program or batch file'.
  • La extensión de script personalizado se ejecuta con la cuenta LocalSystem.
  • Si tiene previsto usar las propiedades storageAccountName y storageAccountKey, deben colocarse en protectedSettings.
  • Solo puede tener aplicada una versión de una extensión a la máquina virtual. Para ejecutar un segundo script personalizado, puede actualizar la extensión existente con una nueva configuración. Alternativamente, puede quitar la extensión de script personalizado y volver a aplicarla con el script actualizado

Esquema de extensión

La configuración de la extensión de script personalizado especifica aspectos como la ubicación del script y el comando que se ejecutará. Esta configuración se puede almacenar en archivos de configuración, o se puede especificar en la línea de comandos o en una plantilla de Azure Resource Manager.

Puede almacenar datos confidenciales en una configuración protegida, que se cifra y se descifra solo dentro de la máquina virtual. La configuración protegida es útil cuando el comando de ejecución incluye secretos, como una contraseña o una referencia a un archivo de firma de acceso compartido (SAS). Este es un ejemplo:

{
    "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"
            ]
        }
    }
}

Nota

La propiedad managedIdentityno debe usarse junto con la propiedad storageAccountName o storageAccountKey.

Solo se puede instalar una versión de una extensión en una máquina virtual a la vez. Se produce un error si se especifica dos veces un script personalizado en la misma plantilla de Azure Resource Manager para la misma máquina virtual.

Puede usar este esquema dentro del recurso de VM o como un recurso independiente. El nombre del recurso debe tener el formato virtualMachineName/extensionName si se usa esta extensión como recurso independiente en la plantilla de Azure Resource Manager.

Valores de propiedad

Nombre Valor o ejemplo Tipo de datos
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 array
timestamp 123456789 Entero de 32 bits
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" } o { "objectId": "12dd289c-0583-46e5-b9b4-115d5c19ef4b" } Objeto JSON

Nota

Los nombres de propiedad distinguen entre mayúsculas y minúsculas. Para evitar problemas de implementación, use los nombres como se muestran aquí.

Detalles del valor de propiedad

Propiedad Opcional u obligatoria Detalles
fileUris Opcionales Direcciones URL de los archivos que se descargarán. Si las direcciones URL son confidenciales, por ejemplo, contienen claves, este campo debe especificarse en protectedSettings.
commandToExecute Obligatorio Script de punto de entrada que se ejecutará. Use esta propiedad si el comando contiene secretos, como contraseñas, o si los URI de archivo son confidenciales.
timestamp Opcionales Cambie este valor solo para desencadenar una nueva ejecución del script. Cualquier valor entero es aceptable, siempre y cuando sea diferente del valor anterior.
storageAccountName Opcionales Nombre de la cuenta de almacenamiento. Si especifica credenciales de almacenamiento, todos los valores de fileUris deben ser direcciones URL de blobs de Azure.
storageAccountKey Opcionales Clave de acceso de la cuenta de almacenamiento.
managedIdentity Opcionales La identidad administrada para descargar archivos. Los valores válidos son clientId (opcional, cadena), que es el identificador de cliente de la identidad administrada y objectId (opcional, cadena), que es el identificador de objeto de la identidad administrada.

La configuración pública se envía en texto no cifrado a la máquina virtual donde se ejecuta el script. La configuración protegida se cifra con una clave que solo conocen Azure y la máquina virtual. La configuración se guarda en la máquina virtual a medida que se envía. Es decir, si la configuración se ha cifrado, se guarda cifrada en la máquina virtual. El certificado que se usa para descifrar los valores cifrados se almacena en la máquina virtual. El certificado también se usa para descifrar la configuración (si es necesario) en tiempo de ejecución.

El empleo de la configuración pública puede resultar útil para la depuración, pero se recomienda usar la configuración protegida.

Puede establecer los siguientes valores en la configuración pública o en la protegida. La extensión rechaza cualquier configuración en la que estos valores estén establecidos tanto en la configuración pública como en la protegida.

  • commandToExecute
  • fileUris

Propiedad: managedIdentity

Nota

Esta propiedad debe especificarse solo en la configuración protegida.

La extensión de script personalizado, versión 1.10 y posteriores, admite identidades administradas para descargar archivos de direcciones URL proporcionadas en la configuración fileUris. La propiedad permite a la extensión de script personalizado acceder a blobs o contenedores privados de Azure Storage sin que el usuario tenga que enviar secretos como tokens de SAS o claves de cuenta de almacenamiento.

Para usar esta característica, agregue una identidad asignada por el sistema o asignada por el usuario a la máquina virtual o al conjunto de escalado de máquinas virtuales donde se ejecuta la extensión de script personalizado. Después, conceda a la identidad administrada acceso al contenedor o blob de Azure Storage.

Para usar la identidad asignada por el sistema en la máquina virtual o conjunto de escalado de máquinas virtuales de destino, establezca el campo managedidentity en un objeto JSON vacío.

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

Para usar la identidad asignada por el usuario en la máquina virtual o el conjunto de escalado de máquinas virtuales de destino, configure el campo managedidentity con el id. de cliente o el id. de objeto de la identidad administrada.

{
  "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" }
}

Nota

La propiedad managedIdentityno debe usarse junto con la propiedad storageAccountName o storageAccountKey.

Implementación de plantilla

Puede implementar extensiones de VM de Azure mediante plantillas de Azure Resource Manager. El esquema JSON detallado en la sección anterior se puede usar en una plantilla de Azure Resource Manager para ejecutar la extensión de script personalizado durante la implementación de la plantilla. En los ejemplos siguientes se muestra cómo usar la extensión de script personalizado:

Implementación de PowerShell

Puede usar el comando Set-AzVMCustomScriptExtension para agregar la extensión de script personalizado a una máquina virtual existente. Para más información, consulte Set-AzVMCustomScriptExtension.

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

Ejemplos

Empleo de varios scripts

En este ejemplo se usan tres scripts para compilar el servidor. La propiedad commandToExecute llama al primer script. Después, dispone de opciones sobre cómo se llama a los demás. Por ejemplo, puede tener un script principal que controla la ejecución con el control de errores, el registro y la administración de estado adecuados. Los scripts se descargan en el equipo local para su ejecución.

Por ejemplo, en 1_Add_Tools.ps1, llamaría a 2_Add_Features.ps1 mediante la adición de .\2_Add_Features.ps1 al script. Repita este proceso para los demás scripts que defina en $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;

Ejecutar scripts desde un recurso compartido local

En este ejemplo, es posible que quiera usar un servidor de Bloque de mensajes del servidor (SMB) local para la ubicación del script. No es necesario proporcionar después ningún otro valor de configuración, excepto 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

Ejecución de un script personalizado más de una vez mediante la CLI

El controlador de extensión de script personalizado impide volver a ejecutar un script si se ha pasado exactamente la misma configuración. Este comportamiento impide que se vuelva a ejecutar accidentalmente, lo que podría provocar comportamientos inesperados si el script no es idempotente. Para confirmar si el controlador bloqueó el nueva ejecución, examine C:\WindowsAzure\Logs\Plugins\Microsoft.Compute.CustomScriptExtension\<HandlerVersion>\CustomScriptHandler.log*. Busque una advertencia como esta:

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

Si quiere ejecutar la extensión de script personalizado más de una vez, puede hacerlo únicamente si se cumplen las condiciones siguientes:

  • El parámetro Name de la extensión coincide con la implementación anterior de la extensión.
  • Ha actualizado la configuración. Puede agregar una propiedad dinámica en el comando, como una marca de tiempo. Si el controlador detecta un cambio en las opciones de configuración, lo considera un deseo explícito de volver a ejecutar el script.

También puede establecer la propiedad ForceUpdateTag en true.

Empleo de Invoke-WebRequest

Si usa Invoke-WebRequest en el script, debe especificar el parámetro -UseBasicParsing. Si no especifica el parámetro, recibe el siguiente error al comprobar el estado detallado:

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.

Virtual Machine Scale Sets

Si implementa la extensión de script personalizado desde Azure Portal, no tiene control sobre la expiración del token de SAS para acceder al script en la cuenta de almacenamiento. La implementación inicial funciona, pero cuando expira el token de SAS de la cuenta de almacenamiento, se produce un error en cualquier operación de escalado posterior porque la extensión de script personalizado ya no puede acceder a la cuenta de almacenamiento.

Se recomienda usar PowerShell, la CLI de Azure o una plantilla de Azure Resource Manager al implementar la extensión de script personalizado en un conjunto de escalado de máquinas virtuales. De este modo, puede elegir usar una identidad administrada o tener control directo de la expiración del token de SAS para acceder al script en la cuenta de almacenamiento durante el tiempo que necesite.

Solución de problemas y asistencia

Puede recuperar los datos sobre el estado de las implementaciones de extensiones desde Azure Portal y mediante el módulo de Azure PowerShell. Para ver el estado de implementación de las extensiones de una máquina virtual, ejecute el comando siguiente:

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

La salida de la extensión se registra en archivos que se encuentran en la siguiente carpeta de la máquina virtual de destino:

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

Los archivos especificados se descargan en la siguiente carpeta de la máquina virtual de destino:

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

En la ruta de acceso anterior, <n> es un entero decimal que puede cambiar entre las ejecuciones de la extensión. El valor 1.* coincide con el valor typeHandlerVersion actual y real de la extensión. Por ejemplo, el directorio real podría ser C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.8\Downloads\2.

Cuando se ejecute el comando commandToExecute, la extensión establece este directorio, por ejemplo, ...\Downloads\2, como directorio de trabajo actual. Este proceso permite el uso de rutas de acceso relativas para buscar los archivos descargados mediante la propiedad fileURIs. Estos son algunos ejemplos de archivos descargados:

URI en fileUris Ubicación de descarga relativa Ubicación de descarga absoluta
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

Las rutas de acceso absolutas al directorio cambian a lo largo del ciclo de vida de la máquina virtual, pero no durante una ejecución única de la extensión de script personalizado.

Dado que la ruta de acceso absoluta de descarga podría variar con el paso del tiempo, es mejor optar por rutas de acceso relativas de archivo o script en la cadena commandToExecute, siempre que sea posible. Por ejemplo:

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

Se conserva la información de la ruta de acceso tras el primer segmento de URI de los archivos descargados mediante la lista de propiedades fileUris. Como se muestra en la tabla anterior, los archivos descargados se asignan en los subdirectorios de descarga para reflejar la estructura de los valores fileUris.

Soporte técnico