Ejecutar scripts de Windows PowerShell desde archivos de proyecto de MSBuild

por Jason Lee

En este tema se describe cómo ejecutar un script de Windows PowerShell como parte de un proceso de compilación e implementación. Puede ejecutar un script localmente (en otras palabras, en el servidor de compilación) o de forma remota, como en un servidor web de destino o servidor de base de datos.

Hay muchas razones por las que es posible que quiera ejecutar un script posterior a la implementación de Windows PowerShell. Por ejemplo, puedes:

  • Agregar un origen de eventos personalizado al registro.
  • Generar un directorio del sistema de archivos para cargas.
  • Limpiar los directorios de compilación.
  • Escribir entradas en un archivo de registro personalizado.
  • Enviar correos electrónicos que inviten a los usuarios a una aplicación web recién aprovisionada.
  • Crear cuentas de usuario con los permisos adecuados.
  • Configurar la replicación entre instancias de SQL Server.

En este tema se muestra cómo ejecutar scripts de Windows PowerShell de forma local y remota desde un destino personalizado en un archivo de proyecto de Microsoft Build Engine (MSBuild).

Este tema forma parte de una serie de tutoriales basados en los requisitos de implementación empresarial de una empresa ficticia denominada Fabrikam, Inc. Esta serie de tutoriales utiliza una solución de muestra (Contact Manager) para representar una aplicación web con un nivel de complejidad realista, que incluye una aplicación ASP.NET MVC 3, un servicio Windows Communication Foundation (WCF) y un proyecto de base de datos.

El método de implementación que constituye el núcleo de estos tutoriales se basa en el enfoque del archivo de proyecto dividido descrito en Descripción del archivo del proyecto, en el que el proceso de compilación está controlado por dos archivos de proyecto: uno que contiene las instrucciones de compilación que se aplican a todos los entornos de destino y otro que contiene los ajustes de compilación e implementación específicos del entorno. En tiempo de compilación, el archivo de proyecto específico del entorno se combina en el archivo de proyecto independiente del entorno para formar un conjunto completo de instrucciones de compilación.

Información general sobre tareas

Para ejecutar un script de Windows PowerShell como parte de un proceso de implementación automatizado o de un solo paso, deberá completar estas tareas de nivel general:

  • Agregar el script de Windows PowerShell a la solución y al control de código fuente.
  • Crear un comando que invoque el script de Windows PowerShell.
  • Escapar los caracteres XML reservados en el comando.
  • Crear un destino en el archivo de proyecto de MSBuild personalizado y usar la tarea Exec para ejecutar el comando.

En este tema se explica cómo realizar estos procedimientos. En las tareas y tutoriales de este tema se asume que ya está familiarizado con las propiedades y destinos de MSBuild, y que comprende cómo usar un archivo de proyecto de MSBuild personalizado para impulsar un proceso de compilación e implementación. Para obtener más información, vea Descripción del archivo de proyecto y Descripción del proceso de compilación.

Crear y agregar scripts de Windows PowerShell

Las tareas de este tema usan un script de Windows PowerShell de ejemplo denominado LogDeploy.ps1 para ilustrar cómo ejecutar scripts desde MSBuild. El script LogDeploy.ps1 contiene una función sencilla que escribe una entrada de una sola línea en un archivo de registro:

function LogDeployment
{
  param([string]$filepath,[string]$deployDestination)
  $datetime = Get-Date
  $filetext = "Deployed package to " + $deployDestination + " on " + $datetime
  $filetext | Out-File -filepath $filepath -Append
}

LogDeployment $args[0] $args[1]

El script LogDeploy.ps1 acepta dos parámetros. El primer parámetro representa la ruta de acceso completa al archivo de registro al que desea agregar una entrada y el segundo parámetro representa el destino de implementación que desea registrar en el archivo de registro. Al ejecutar el script, agrega una línea al archivo de registro en este formato:

Deployed package to TESTWEB1 on 02/11/2012 09:28:18

Para que el script LogDeploy.ps1 esté disponible para MSBuild, debe:

  • Agregar el script al control de código fuente.
  • Agregar el script a la solución en Visual Studio 2010.

No es necesario implementar el script con el contenido de la solución, independientemente de si planea ejecutar el script en el servidor de compilación o en un equipo remoto. Una opción es agregar el script a una carpeta de solución. En el ejemplo de Contact Manager (Administrador de soluciones), dado que quiere usar el script de Windows PowerShell como parte del proceso de implementación, tiene sentido agregar el script a la carpeta Publish Solution (Publicar solución).

In the Contact Manager example, because you want to use the Windows PowerShell script as part of the deployment process, it makes sense to add the script to the Publish solution folder.

El contenido de las carpetas de soluciones se copia en servidores de compilación como material de origen. Sin embargo, no forma parte de ninguna salida del proyecto.

Ejecución de un script de Windows PowerShell en el servidor de compilación

En algunos escenarios, es posible que quiera ejecutar scripts de Windows PowerShell en el equipo que compila los proyectos. Por ejemplo, puede usar un script de Windows PowerShell para limpiar carpetas de compilación o escribir entradas en un archivo de registro personalizado.

En términos de sintaxis, ejecutar un script de Windows PowerShell desde un archivo de proyecto de MSBuild es lo mismo que ejecutar un script de Windows PowerShell desde un símbolo del sistema normal. Debe invocar el archivo ejecutable powershell.exe y usar el modificador –command para proporcionar los comandos que quiere que ejecute Windows PowerShell. (En Windows PowerShell v2, también puede usar el modificador –file). El comando debe tener este formato:

powershell.exe –command "& { [Path to script] 'parameter1' 'parameter2' ... }"

Por ejemplo:

powershell.exe –command 
  "& { C:\LogDeploy.ps1 'C:\DeployLogs\log.txt' 'TESTWEB1' }"

Si la ruta de acceso al script incluye espacios, debe incluir la ruta de acceso del archivo entre comillas simples precedidas por una y comercial. No puede usar comillas dobles, ya que ya las ha usado para incluir el comando:

powershell.exe –command 
  "& { &'C:\Path With Spaces\LogDeploy.ps1' 
        'C:\Path With Spaces\log.txt' 
        'TESTWEB1' }"

Hay algunas consideraciones adicionales al invocar este comando desde MSBuild. En primer lugar, debe incluir la marca –NonInteractive para asegurarse de que el script se ejecuta silenciosamente. A continuación, debe incluir la marca –ExecutionPolicy con un valor de argumento adecuado. Esto especifica la directiva de ejecución que Windows PowerShell aplicará al script y le permite invalidar la directiva de ejecución predeterminada, lo cual puede impedir la ejecución del script. Puede elegir entre estos valores de argumento:

  • Un valor Unrestricted permitirá que Windows PowerShell ejecute el script, independientemente de si el script está firmado.
  • Un valor RemoteSigned permitirá a Windows PowerShell ejecutar scripts sin firmar creados en el equipo local. Sin embargo, los scripts creados en otro lugar deben estar firmados. (En la práctica, es muy poco probable que haya creado un script de Windows PowerShell localmente en un servidor de compilación).
  • Un valor AllSigned permitirá que Windows PowerShell ejecute solo scripts firmados.

La directiva de ejecución predeterminada está restringida, lo cual impide que Windows PowerShell ejecute archivos de script.

Por último, debe escapar los caracteres XML reservados que se produzcan en el comando de Windows PowerShell:

  • Reemplazar las comillas simples por '

  • Reemplazar las comillas dobles por "

  • Reemplazar las y comerciales por &

  • Al realizar estos cambios, el comando será similar al siguiente:

powershell.exe –NonInteractive –ExecutionPolicy Unrestricted 
               –command "& { &'[Path to script]' 
                        '[parameter1]' 
                        '[parameter2]' } "

En el archivo de proyecto de MSBuild personalizado, puede crear un nuevo destino y usar la tarea Exec para ejecutar este comando:

<Target Name="WriteLogEntry" Condition=" '$(WriteLogEntry)'!='false' ">
  <PropertyGroup>
    <PowerShellExe Condition=" '$(PowerShellExe)'=='' "> 
      %WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe
    </PowerShellExe>
    <ScriptLocation Condition=" '$(ScriptLocation)'=='' ">
      C:\Path With Spaces\LogDeploy.ps1
    </ScriptLocation>
    <LogFileLocation Condition=" '$(LogFileLocation)'=='' ">
      C:\Path With Spaces\ContactManagerDeployLog.txt
    </LogFileLocation>
  </PropertyGroup>
  <Exec Command="$(PowerShellExe) -NonInteractive -executionpolicy Unrestricted 
                 -command &quot;&amp; { 
                          &amp;&apos;$(ScriptLocation)&apos; 
                          &apos;$(LogFileLocation)&apos; 
                          &apos;$(MSDeployComputerName)&apos;} &quot;" />
</Target>

En este ejemplo, tenga en cuenta que:

  • Las variables, como los valores de parámetro y la ubicación del ejecutable de Windows PowerShell, se declaran como propiedades de MSBuild.
  • Se incluyen condiciones para permitir que los usuarios invaliden estos valores desde la línea de comandos.
  • La propiedad MSDeployComputerName se declara en otro lugar del archivo del proyecto.

Al ejecutar este destino como parte del proceso de compilación, Windows PowerShell ejecutará el comando y escribirá una entrada de registro en el archivo especificado.

Ejecución de un script de Windows PowerShell en un equipo remoto

Windows PowerShell puede ejecutar scripts en equipos remotos a través de la Administración remota de Windows (WinRM). Para ello, debe usar el cmdlet Invoke-Command. Esto le permite ejecutar el script en uno o varios equipos remotos sin copiar el script en los equipos remotos. Los resultados se devuelven al equipo local desde el que se ejecutó el script.

Nota:

Antes de usar el cmdlet Invoke-Command para ejecutar scripts de Windows PowerShell en un equipo remoto, debe configurar un agente de escucha WinRM para aceptar mensajes remotos. Para ello, ejecute el comando winrm quickconfig en el equipo remoto. Para obtener más información, consulte Installation and Configuration for Windows Remote Management.

Desde una ventana de Windows PowerShell, usará esta sintaxis para ejecutar el script LogDeploy.ps1 en un equipo remoto:

Invoke-Command –ComputerName 'REMOTESERVER1' 
               –ScriptBlock { &"C:\Path With Spaces\LogDeploy.ps1"
                               'C:\Path With Spaces\Log.txt'
                               'TESTWEB1' }

Nota:

Hay otras maneras de usar Invoke-Command para ejecutar un archivo de script, pero este enfoque es el más sencillo cuando necesita proporcionar valores de parámetro y administrar rutas de acceso con espacios.

Al ejecutarlo desde un símbolo del sistema, debe invocar el ejecutable de Windows PowerShell y usar el parámetro –command para proporcionar las instrucciones:

powershell.exe –command 
  "& {Invoke-Command –ComputerName 'REMOTESERVER1' 
                     –ScriptBlock { &'C:\Path With Spaces\LogDeploy.ps1'
                                     'C:\Path With Spaces\Log.txt'
                                     'TESTWEB1' } "

Como antes, debe proporcionar algunos modificadores adicionales y escapar los caracteres XML reservados al ejecutar el comando desde MSBuild:

powershell.exe -NonInteractive -executionpolicy Unrestricted 
               -command &quot;&amp; Invoke-Command 
                 –ComputerName &apos;REMOTESERVER1&apos;
                 -ScriptBlock { &amp;&apos;C:\Path With Spaces\LogDeploy.ps1&apos; 
                                &apos; C:\Path With Spaces\Log.txt &apos;  
                                &apos;TESTWEB1&apos; } &quot;

Por último, como antes, puede usar la tarea Exec dentro de un destino de MSBuild personalizado para ejecutar el comando:

<Target Name="WriteLogEntry" Condition=" '$(WriteLogEntry)'!='false' ">
  <PropertyGroup>
    <PowerShellExe Condition=" '$(PowerShellExe)'=='' "> 
      %WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe
    </PowerShellExe>
    <ScriptLocation Condition=" '$(ScriptLocation)'=='' ">
      C:\Path With Spaces\LogDeploy.ps1
    </ScriptLocation>
    <LogFileLocation Condition=" '$(LogFileLocation)'=='' ">
      C:\Path With Spaces\ContactManagerDeployLog.txt
    </LogFileLocation>
  </PropertyGroup>
  <Exec Command="$(PowerShellExe) -NonInteractive -executionpolicy Unrestricted 
                 -command &quot;&amp; invoke-command -scriptblock { 
                          &amp;&apos;$(ScriptLocation)&apos; 
                          &apos;$(LogFileLocation)&apos;  
                          &apos;$(MSDeployComputerName)&apos;}
                          &quot;"/>  
</Target>

Al ejecutar este destino como parte del proceso de compilación, Windows PowerShell ejecutará el script en el equipo que especificó en el argumento –computername.

Conclusión

En este tema se describe cómo ejecutar un script de Windows PowerShell desde un archivo de proyecto de MSBuild. Puede usar este enfoque para ejecutar un script de Windows PowerShell, ya sea localmente o en un equipo remoto, como parte de un proceso automatizado o de compilación e implementación de un solo paso.

Lecturas adicionales

Para obtener instrucciones sobre cómo firmar scripts de Windows PowerShell y administrar directivas de ejecución, consulte Ejecución de scripts de Windows PowerShell. Para obtener instrucciones sobre cómo ejecutar comandos de Windows PowerShell desde un equipo remoto, consulte Ejecutar comandos remotos.

Para obtener más información sobre el uso de archivos de proyecto de MSBuild personalizados para controlar el proceso de implementación, vea Descripción del archivo del proyecto y Descripción del proceso de compilación.