PowerShell 配置器

原文


类型:powershell

Packer 的 PowerShell 配置器在 Windows 机器上运行 PowerShell 脚本。它默认使用的通信器是 WinRM。然而,当与 SSH 通信器结合使用时,配置器可以同样工作(有一些注意事项)。有关详细信息,请参阅下面的部分

注意:如果可能,尽量始终使用斜杠 / 作为路径分隔符,尤其是在处理相对路径时。反斜杠 \ 可以用于 Windows,并且是官方的 Windows 路径分隔符,但是当从任何非 Windows 系统构建镜像时,Packer 只会将斜杠 / 视为路径分隔符,并将反斜杠视为纯文本。这可能会导致路径错误。

基础用法

HCL2:

provisioner "powershell" {
  inline = ["dir c:/"]
}

Json:

{
  "type": "powershell",
  "inline": ["dir c:/"]
}

配置参数

下面列出了可用配置选项的参考。唯一必须的元素是 inlinescript,此外的其他参数都是可选的。

以下参数必须选一配置:

  • inline(array of string)- 这是一组要执行的命令。这些命令由换行符连接起来并变成一个文件,因此它们都在相同的上下文中执行。这使得您可以在一个命令中更改目录,并在下一个命令中使用目录中的内容,依此类推。内联脚本是在机器内完成简单任务的最简单方法。
  • script (string) - 要上传到机器并执行的脚本的路径。此路径可以是绝对路径或相对路径。如果是相对路径,就是相对于Packer执行时的工作目录。
  • scripts(array of string)- 要执行的脚本数组。脚本将按照指定的顺序上传和执行。每个脚本都是独立执行的,因此一个脚本中的变量等状态不会传递到下一个脚本。

选填参数

  • binary (boolean) - 如果为 true,则指定脚本是二进制文件,因此 Packer 不应将 Windows 行结尾转换为 Unix 行结尾(如果有的话)。默认为 false
  • valid_exit_codes(整数列表)- 脚本的有效返回值。默认情况下是 0。
  • debug_mode - 如果配置该参数,则设置 PowerShell 的 PSDebug 模式以使脚本调试更容易。例如,将值设置为 1 会将下面的命令添加到执行命令中:
Set-PSDebug -Trace 1
  • elevated_execute_command(string)- 用于执行提权脚本的命令。默认情况下,如下所示:

powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"

由于配置器是一个模板引擎。因此,您可以在此字段中使用用户变量和模板函数。此外,您可以使用两个额外的变量:

  • Path:要运行的脚本的路径
  • Vars:包含 environment_vars 列表的临时文件的位置(如果已配置)。PathVars 的值都可以通过分别设置 remote_pathremote_env_var_path 的值来手动配置。

    如果您使用 SSH 通信器并更改了默认 shell,您可能需要修改 execute_command 以确保该命令有效且正确转义;默认将使用 cmd

  • env(map(string))- 在 execute_command 之前注入的键/值对映射。 Packer 也会默认将一些环境变量注入到环境中,这将在下面的部分中介绍。重复的 env 设置会覆盖 environment_vars 中的设置。这不是启用 JSON 模板引擎的函数。 HCL 插值照常工作。
  • environment_vars([]string)- 在 execute_command 之前注入的键/值对数组。格式应为key=value。 Packer 也会默认将一些环境变量注入到环境中,这将在下面的部分中介绍。
  • use_pwsh(bool)- 运行 pwsh.exe 而不是 powershell.exe。默认为 false。 该值是一个模板引擎。因此,您可以在此字段中使用用户变量和模板函数。如果您在 AWS、Azure、Google Compute 或 OpenStack 上运行并且想要访问 Packer 用于通过 WinRM 连接到实例的自动生成的密码,您可以使用构建模板引擎通过 { { build `Password` } } 注入密码。在 HCL 模板中,您可以通过访问构建变量来做同样的事情,例如:

HCL2:

provisioner "powershell" {
  environment_vars = ["WINRMPASS=${build.Password}"]
  inline = ["Write-Host \"Automatically generated aws password is: $Env:WINRMPASS\""]
}

Json:

{
  "type": "powershell",
  "environment_vars": ["WINRMPASS={ { build `Password` } }"],
  "inline": ["Write-Host \"Automatically generated aws password is: $Env:WINRMPASS\""]
},
  • execute_command (string) - 用于执行脚本的命令。默认情况下,如下所示:

powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"

该值是一个模板引擎。因此,您可以在此字段中使用用户变量和模板函数。此外,您可以使用两个额外的变量:

  • Path:要运行的脚本的路径
  • Vars:包含 environment_vars 列表的临时文件的位置(如果已配置)。 PathVars 的值都可以通过分别设置 remote_pathremote_env_var_path 的值来手动配置。 如果您使用 SSH 通信器并更改了默认 shell,您可能需要修改 execute_command 以确保该命令有效且正确转义;默认假定您没有将默认 shell 更改为 cmd
  • elevated_userelevated_pa​​ssword(string)- 如果指定,PowerShell 脚本将使用给定的 Windows 用户以提权模式运行。 该值是一个模板引擎。因此,您可以在此字段中使用用户变量和模板函数。如果您在 AWS、Azure、Google Compute 或 OpenStack 上运行并且想要访问 Packer 用于通过 WinRM 连接到实例的自动生成的密码,您可以使用构建模板引擎通过 { { build `Password` } } 注入密码。在 HCL 模板中,您可以通过访问构建变量来完成相同的功能,例如:

    HCL2:

provisioner "powershell" {
  elevated_user = "Administrator"
  elevated_password = build.Password
}

Json:

{
  "type": "powershell",
  "elevated_user": "Administrator",
  "elevated_password": "{ { build `Password` } }",
  ...
},

如果您指定了空的 elevated_pa​​ssword 值,则 PowerShell 脚本将作为服务帐户运行。例如:

HCL2:

provisioner "powershell" {
  elevated_user = "SYSTEM"
  elevated_password = ""
}

Json:

{
  "type": "powershell",
  "elevated_user": "SYSTEM",
  "elevated_password": "",
  ...
},
  • execution_policy - 要在 Windows 上运行 PowerShell 脚本,Packer 默认将此设置为 "bypass" 并包装要运行的命令。将此设置为 "none" 将防止包装,允许在 Windows 上看到 Docker 的退出代码。可能的值为 bypassallsigneddefaultremotesignedrestrictedundefinedunrestrictednone
  • remote_path (string) - PowerShell 脚本将被上传到目标构建机器中的路径。默认为 C:/Windows/Temp/script-UUID.ps1,其中 UUID 替换为动态生成的唯一标识脚本的字符串。 此设置允许用户覆盖默认上传位置。该值必须是可写位置,并且所有父目录必须已经存在。
  • remote_env_var_path(string)- 通过 PowerShell 脚本中上传所需的环境变量到远程环境中,然后在通过“dot sourcing”脚本执行主命令或脚本。 环境变量脚本将上传到的路径默认为 C:/Windows/Temp/packer-ps-env-vars-UUID.ps1,其中 UUID 被替换为动态生成的唯一标识脚本的字符串。 此设置允许用户改写环境变量脚本上传到的位置。该值必须是可写位置,并且所有父目录必须已经存在。
  • skip_clean (bool) - 是否在执行配置器后清理脚本。默认为 false。当为 true 时,任何由非提权的 Powershell 配置器创建的脚本都将从远程计算机中删除。无论为 skip_clean 设置的值如何,提权的脚本以及规划的任务将始终被删除。
  • start_retry_timeout (string) - 尝试启动远程进程的延时。默认情况下,这是 5m 或 5 分钟。此设置的存在是为了处理 SSH 可能重新启动的时间,例如系统重新启动。如果重新启动需要更长的时间,请将此设置为更高的值。
  • pause_after (string) - 配置 PowerShell 脚本后等待的时间,如果前面的所有步骤都成功了,则暂停。

所有配置器共有的参数:

  • pause_before (duration) - 执行前休眠一段时间。
  • max_retries (int) - provisioner 在失败的情况下重试的最大次数。默认为零 (0)。零表示不会重试错误。
  • only (array of string) - 只运行列表中指定的的配置器程序。
  • override (object) - 使用特定配置器的不同设置覆盖配置器,例如:

HCL2:

source "null" "example1" {
  communicator = "none"
}

source "null" "example2" {
  communicator = "none"
}

build {
  sources = ["source.null.example1", "source.null.example2"]
  provisioner "shell-local" {
    inline = ["echo not overridden"]
    override = {
      example1 = {
        inline = ["echo yes overridden"]
      }
    }
  }
}

Json:

{
  "builders": [
    {
      "type": "null",
      "name": "example1",
      "communicator": "none"
    },
    {
      "type": "null",
      "name": "example2",
      "communicator": "none"
    }
  ],
  "provisioners": [
    {
      "type": "shell-local",
      "inline": ["echo not overridden"],
      "override": {
        "example1": {
          "inline": ["echo yes overridden"]
        }
      }
    }
  ]
}
  • timeout(duration)- 如果配置器完成时间超过配置值(例如 1h10m1s10m),则配置器将超时并失败。

默认的环境变量

除了能够使用 environment_vars 配置自定义环境变量外,配置器还会自动定义某些常用的环境变量:

  • PACKER_BUILD_NAME 值为 Packer 正在运行的构建的名称。这在 Packer 进行多个构建并且您希望将它们彼此能有所区分时最有用。
  • PACKER_BUILDER_TYPE 是用于创建运行脚本的机器的构建器类型。如果您只想在使用特定构建器构建的系统上运行脚本的特定部分,这将很有用。
  • PACKER_HTTP_ADDR 如果使用为文件传输提供 HTTP 服务器的构建器(例如 hypervparallelsqemuvirtualboxvmware),这将被设置为 HTTP 服务器地址。您可以在配置器中使用此地址通过 HTTP 下载大文件。如果您在使用默认文件配置器时遇到较慢的速度,这可能很有用。使用 winrm 通信器的 file 配置器可能会遇到这些类型的困难。

将 PowerShell Provisioner 与 SSH Communicator 相结合

先说好消息。如果您使用的是 Microsoft 的 OpenSSH 移植版本,那么配置程序应该会按预期工作 —— 不需要额外的配置工作。

下面是警告。如果您使用的是替代配置,并且您的 SSH 连接使您进入远程主机上的 *nix shell,那么您很可能需要手动设置 execute_command; Packer 使用的默认 execute_command 对您不起作用。配置命令时,您需要确保相应地转义任何可能被远程 shell 错误解释的 $ 符号或其他字符。

以下示例显示了如何重新配置​​标准 execute_command 以在安装了 Cygwin/OpenSSH 的远程系统上工作。 execute_command 对每个 $ 符号反斜杠进行了转义,这样它就不会被远程 Bash shell 解释——Bash 是 Cygwin 环境的默认 shell。

HCL2:


provisioner "powershell" {
    execute_command = "powershell -executionpolicy bypass \"& { if (Test-Path variable:global:ProgressPreference){\\$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit \\$LastExitCode }\""
    inline          = [ "Write-Host \"Hello from PowerShell\""]
}

Json:


"provisioners": [
  {
    "type": "powershell",
    "execute_command": "powershell -executionpolicy bypass \"& { if (Test-Path variable:global:ProgressPreference){\\$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit \\$LastExitCode }\"",
    "inline": ["Write-Host \"Hello from PowerShell\""]
  }
]

Packer 对 PowerShell 特殊字符的处理

PowerShell 中的转义字符是 backtick(反引号),有时也称为 grave accent(重音符)。何时以及何时不转义 PowerShell 的特殊字符可能最好通过一系列示例来说明。

需要转义的情况

当 PowerShell 特有的转义字符直接出现在 PowerShell 配置器的 inline 命令中,或者当它们直接出现在用户自己的脚本中时,用户需要处理 PowerShell 这些转义字符。请注意,如果 Json 模板中的双引号内出现双引号,则需要添加反斜杠转义才能正确解析。

HCL2:

provisioner "powershell" {
    inline = [
        "Write-Host \"A literal dollar `$ must be escaped\"",
        "Write-Host \"A literal backtick `` must be escaped\"",
        "Write-Host \"Here `\"double quotes`\" must be escaped\"",
        "Write-Host \"Here `'single quotes`' don`'t really need to be\"",
        "Write-Host \"escaped... but it doesn`'t hurt to do so.\"",
    ]
}

Json:

  "provisioners": [
    {
      "type": "powershell",
      "inline": [
          "Write-Host \"A literal dollar `$ must be escaped\"",
          "Write-Host \"A literal backtick `` must be escaped\"",
          "Write-Host \"Here `\"double quotes`\" must be escaped\"",
          "Write-Host \"Here `'single quotes`' don`'t really need to be\"",
          "Write-Host \"escaped... but it doesn`'t hurt to do so.\""
      ]
    }
  ]

上面的代码片段应该会在 Packer 控制台上产生以下输出:

==> amazon-ebs: Provisioning with Powershell...
==> amazon-ebs: Provisioning with PowerShell script: /var/folders/15/d0f7gdg13rnd1cxp7tgmr55c0000gn/T/packer-powershell-provisioner508190439
    amazon-ebs: A literal dollar $ must be escaped
    amazon-ebs: A literal backtick ` must be escaped
    amazon-ebs: Here "double quotes" must be escaped
    amazon-ebs: Here 'single quotes' don't really need to be
    amazon-ebs: escaped... but it doesn't hurt to do so.

无需转义的情况

用户环境变量值以及 elevated_userelevated_pa​​ssword 字段中出现的特殊字符将自动为用户处理。在这些情况下无需使用转义符。

HCL2:

variable "psvar" {
  type    = string
  default = "My$tring"
}

build {
  sources = ["source.amazon-ebs.example"]

  provisioner "powershell" {
      elevated_user     = "Administrator"
      elevated_password = "Super$3cr3t!"
      inline            = ["Write-Output \"The dollar in the elevated_password is interpreted correctly\""]
  }
  provisioner "powershell" {
    environment_vars = [
        "VAR1=A$Dollar",
        "VAR2=A`Backtick",
        "VAR3=A'SingleQuote",
        "VAR4=A\"DoubleQuote",
        "VAR5=${var.psvar}",
    ]
    inline = [
      "Write-Output \"In the following examples the special character is interpreted correctly:\"",
      "Write-Output \"The dollar in VAR1:                            $Env:VAR1\"",
      "Write-Output \"The backtick in VAR2:                          $Env:VAR2\"",
      "Write-Output \"The single quote in VAR3:                      $Env:VAR3\"",
      "Write-Output \"The double quote in VAR4:                      $Env:VAR4\"",
      "Write-Output \"The dollar in VAR5 (expanded from a user var): $Env:VAR5\"",
    ]
  }
}

Json:


{
  "variables": {
    "psvar": "My$tring"
  },
  ...
  "provisioners": [
    {
      "type": "powershell",
      "elevated_user": "Administrator",
      "elevated_password": "Super$3cr3t!",
      "inline": "Write-Output \"The dollar in the elevated_password is interpreted correctly\""
    },
    {
      "type": "powershell",
      "environment_vars": [
        "VAR1=A$Dollar",
        "VAR2=A`Backtick",
        "VAR3=A'SingleQuote",
        "VAR4=A\"DoubleQuote",
        "VAR5={{user `psvar`}}"
      ],
      "inline": [
        "Write-Output \"In the following examples the special character is interpreted correctly:\"",
        "Write-Output \"The dollar in VAR1:                            $Env:VAR1\"",
        "Write-Output \"The backtick in VAR2:                          $Env:VAR2\"",
        "Write-Output \"The single quote in VAR3:                      $Env:VAR3\"",
        "Write-Output \"The double quote in VAR4:                      $Env:VAR4\"",
        "Write-Output \"The dollar in VAR5 (expanded from a user var): $Env:VAR5\""
      ]
    }
  ]
  ...
}

上面的代码片段应该会在 Packer 控制台上产生以下输出:

==> amazon-ebs: Provisioning with Powershell...
==> amazon-ebs: Provisioning with PowerShell script: /var/folders/15/d0f7gdg13rnd1cxp7tgmr55c0000gn/T/packer-powershell-provisioner961728919
    amazon-ebs: The dollar in the elevated_password is interpreted correctly
==> amazon-ebs: Provisioning with Powershell...
==> amazon-ebs: Provisioning with PowerShell script: /var/folders/15/d0f7gdg13rnd1cxp7tgmr55c0000gn/T/packer-powershell-provisioner142826554
    amazon-ebs: In the following examples the special character is interpreted correctly:
    amazon-ebs: The dollar in VAR1:                            A$Dollar
    amazon-ebs: The backtick in VAR2:                          A`Backtick
    amazon-ebs: The single quote in VAR3:                      A'SingleQuote
    amazon-ebs: The double quote in VAR4:                      A"DoubleQuote
    amazon-ebs: The dollar in VAR5 (expanded from a user var): My$tring

results matching ""

    No results matching ""