从 MSBuild 项目文件中运行 Windows PowerShell 脚本

作者 :Jason Lee

本主题介绍如何在生成和部署过程中运行Windows PowerShell脚本。 可以在本地 (运行脚本,换言之,可以在生成服务器上) 或远程运行脚本,例如在目标 Web 服务器或数据库服务器上。

可能需要运行部署后Windows PowerShell脚本的原因有很多。 例如,您可能希望:

  • 将自定义事件源添加到注册表。
  • 生成用于上传的文件系统目录。
  • 清理生成目录。
  • 将条目写入自定义日志文件。
  • 向新预配的 Web 应用程序发送邀请用户的电子邮件。
  • 创建具有相应权限的用户帐户。
  • 配置SQL Server实例之间的复制。

本主题介绍如何从Microsoft 生成引擎 (MSBuild) 项目文件中的自定义目标本地和远程运行Windows PowerShell脚本。

本主题是一系列教程的一部分,这些教程基于名为 Fabrikam, Inc 的虚构公司的企业部署要求。本教程系列使用示例解决方案( Contact Manager 解决方案)来表示具有实际复杂程度的 Web 应用程序,包括 ASP.NET MVC 3 应用程序、Windows Communication Foundation (WCF) 服务和数据库项目。

这些教程的核心部署方法基于了解项目文件中所述的拆分 项目文件方法,其中生成过程由两个项目文件控制,一个项目文件包含适用于每个目标环境的生成说明,另一个包含特定于环境的生成和部署设置。 在生成时,特定于环境的项目文件将合并到与环境无关的项目文件中,形成一组完整的生成指令。

任务概述

若要在自动化或单步部署过程中运行Windows PowerShell脚本,需要完成以下高级任务:

  • 将 Windows PowerShell 脚本添加到解决方案和源代码管理。
  • 创建调用 Windows PowerShell 脚本的命令。
  • 转义命令中的任何保留 XML 字符。
  • 在自定义 MSBuild 项目文件中创建一个目标,并使用 Exec 任务运行命令。

本主题将演示如何执行这些过程。 本主题中的任务和演练假定你已经熟悉 MSBuild 目标和属性,并且你了解如何使用自定义 MSBuild 项目文件来驱动生成和部署过程。 有关详细信息,请参阅了解项目文件和了解生成过程

创建和添加Windows PowerShell脚本

本主题中的任务使用名为LogDeploy.ps1的示例Windows PowerShell脚本来演示如何从 MSBuild 运行脚本。 LogDeploy.ps1 脚本包含一个简单的函数,用于将单行条目写入日志文件:

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]

LogDeploy.ps1 脚本接受两个参数。 第一个参数表示要向其添加条目的日志文件的完整路径,第二个参数表示要在日志文件中记录的部署目标。 运行脚本时,它会以以下格式向日志文件添加一行:

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

若要使 LogDeploy.ps1 脚本可用于 MSBuild,需要:

  • 将脚本添加到源代码管理。
  • 在 Visual Studio 2010 中将脚本添加到解决方案。

无论计划是在生成服务器上还是在远程计算机上运行脚本,都不需要使用解决方案内容部署脚本。 一个选项是将脚本添加到解决方案文件夹。 在联系人管理器示例中,由于你想要在部署过程中使用 Windows PowerShell 脚本,因此最好将脚本添加到“发布解决方案”文件夹。

在联系人管理器示例中,由于你想要在部署过程中使用 Windows PowerShell 脚本,因此最好将脚本添加到“发布解决方案”文件夹。

将解决方案文件夹的内容复制为生成服务器作为源材料。 但是,它们不构成任何项目输出的一部分。

在生成服务器上执行Windows PowerShell脚本

在某些情况下,可能需要在生成项目的计算机上运行Windows PowerShell脚本。 例如,可以使用 Windows PowerShell 脚本来清理生成文件夹或将条目写入自定义日志文件。

就语法而言,从 MSBuild 项目文件运行Windows PowerShell脚本与从常规命令提示符运行Windows PowerShell脚本相同。 需要调用 powershell.exe 可执行文件,并使用 –command 开关提供想要Windows PowerShell运行的命令。 (在 Windows PowerShell v2 中,还可以使用 –file 开关) 。 命令应采用以下格式:

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

例如:

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

如果脚本的路径包含空格,则需要将文件路径括在单引号中,前面有一个和号。 不能使用双引号,因为已使用双引号将命令括起来:

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

从 MSBuild 调用此命令时,还有一些其他注意事项。 首先,应包含 –NonInteractive 标志,以确保脚本以静默方式执行。 接下来,应包含具有适当参数值的 –ExecutionPolicy 标志。 这指定Windows PowerShell将应用于脚本的执行策略,并允许重写可能会阻止脚本执行的默认执行策略。 可以从以下参数值中进行选择:

  • 如果值为 Unrestricted,则无论脚本是否已签名,Windows PowerShell都能够执行脚本。
  • 值 RemoteSigned 将允许Windows PowerShell执行在本地计算机上创建的未签名脚本。 但是,必须在其他位置创建的脚本进行签名。 (实际上,你不太可能在生成服务器) 本地创建Windows PowerShell脚本。
  • AllSigned 值将仅允许Windows PowerShell执行已签名脚本。

默认执行策略为“受限”,可防止Windows PowerShell运行任何脚本文件。

最后,需要转义Windows PowerShell命令中出现的任何保留 XML 字符:

  • 将单引号替换为 '

  • 将双引号替换为 "

  • 将和替换为 &

  • 进行这些更改时,命令将如下所示:

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

在自定义 MSBuild 项目文件中,可以创建新目标并使用 Exec 任务运行此命令:

<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>

在此示例中,请注意:

  • 任何变量(如参数值和Windows PowerShell可执行文件的位置)都声明为 MSBuild 属性。
  • 包括条件,使用户能够从命令行替代这些值。
  • MSDeployComputerName 属性在项目文件的其他位置声明。

在生成过程中执行此目标时,Windows PowerShell将运行命令并将日志条目写入指定的文件。

在远程计算机上执行Windows PowerShell脚本

Windows PowerShell能够通过 Windows 远程管理 (WinRM) 在远程计算机上运行脚本。 为此,需要使用 Invoke-Command cmdlet。 这样,便可以对一台或多台远程计算机执行脚本,而无需将脚本复制到远程计算机。 任何结果将返回到运行脚本的本地计算机。

注意

在使用 Invoke-Command cmdlet 在远程计算机上执行Windows PowerShell脚本之前,需要配置 WinRM 侦听器以接受远程消息。 可以通过在远程计算机上运行 winrm quickconfig 命令来执行此操作。 有关详细信息,请参阅 Windows 远程管理的安装和配置

在Windows PowerShell窗口中,你将使用此语法在远程计算机上运行 LogDeploy.ps1 脚本:

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

注意

使用 Invoke-Command 运行脚本文件有多种其他方法,但当你需要提供参数值并使用空格管理路径时,此方法最为简单。

从命令提示符运行此命令时,需要调用 Windows PowerShell 可执行文件,并使用 –command 参数提供说明:

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

与以前一样,在从 MSBuild 运行命令时,需要提供一些其他开关并转义任何保留的 XML 字符:

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;

最后,可以像以前一样,在自定义 MSBuild 目标中使用 Exec 任务来执行命令:

<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>

在生成过程中执行此目标时,Windows PowerShell将在 –computername 参数中指定的计算机上运行脚本。

结论

本主题介绍了如何从 MSBuild 项目文件运行Windows PowerShell脚本。 可以使用此方法在本地或远程计算机上运行Windows PowerShell脚本,作为自动化或单步生成和部署过程的一部分。

深入阅读

有关Windows PowerShell脚本签名和管理执行策略的指南,请参阅运行Windows PowerShell脚本。 有关从远程计算机运行Windows PowerShell命令的指南,请参阅运行远程命令

有关使用自定义 MSBuild 项目文件来控制部署过程的详细信息,请参阅了解项目文件和了解生成过程