使用 Ansible 和 Windows

当使用 Ansible 管理 Windows 时,许多适用于 Unix/Linux 主机的语法和规则也适用于 Windows,但当涉及到路径分隔符和特定于操作系统的任务等组件时,仍然存在一些差异。本文档涵盖了使用 Ansible for Windows 的具体细节。

用例

Ansible 可用于协调 Windows 服务器上的大量任务。以下是一些示例以及有关常见任务的信息。

安装软件

Ansible 可以通过三种主要方式来安装软件

  • 使用 win_chocolatey 模块。这从默认的公共 Chocolatey 存储库中获取程序数据。可以通过设置 source 选项来使用内部存储库。

  • 使用 win_package 模块。这使用来自本地/网络路径或 URL 的 MSI 或 .exe 安装程序来安装软件。

  • 使用 win_commandwin_shell 模块手动运行安装程序。

建议使用 win_chocolatey 模块,因为它具有最完整的逻辑来检查是否已安装软件包并且是否为最新版本。

以下是一些使用所有三个选项安装 7-Zip 的示例

# Install/uninstall with chocolatey
- name: Ensure 7-Zip is installed through Chocolatey
  win_chocolatey:
    name: 7zip
    state: present

- name: Ensure 7-Zip is not installed through Chocolatey
  win_chocolatey:
    name: 7zip
    state: absent

# Install/uninstall with win_package
- name: Download the 7-Zip package
  win_get_url:
    url: https://www.7-zip.org/a/7z1701-x64.msi
    dest: C:\temp\7z.msi

- name: Ensure 7-Zip is installed through win_package
  win_package:
    path: C:\temp\7z.msi
    state: present

- name: Ensure 7-Zip is not installed through win_package
  win_package:
    path: C:\temp\7z.msi
    state: absent

# Install/uninstall with win_command
- name: Download the 7-Zip package
  win_get_url:
    url: https://www.7-zip.org/a/7z1701-x64.msi
    dest: C:\temp\7z.msi

- name: Check if 7-Zip is already installed
  win_reg_stat:
    name: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{23170F69-40C1-2702-1701-000001000000}
  register: 7zip_installed

- name: Ensure 7-Zip is installed through win_command
  win_command: C:\Windows\System32\msiexec.exe /i C:\temp\7z.msi /qn /norestart
  when: 7zip_installed.exists == false

- name: Ensure 7-Zip is uninstalled through win_command
  win_command: C:\Windows\System32\msiexec.exe /x {23170F69-40C1-2702-1701-000001000000} /qn /norestart
  when: 7zip_installed.exists == true

某些安装程序(如 Microsoft Office 或 SQL Server)需要凭据委托或访问受 WinRM 限制的组件。绕过这些问题的最佳方法是在任务中使用 become。使用 become,Ansible 将运行安装程序,就像它在主机上以交互方式运行一样。

注意

许多安装程序不会通过 WinRM 正确地传回错误信息。在这些情况下,如果已验证安装在本地可以工作,则推荐的方法是使用 become。

注意

某些安装程序会重新启动 WinRM 或 HTTP 服务,或导致它们暂时不可用,从而使 Ansible 认为系统不可访问。

安装更新

可以使用 win_updateswin_hotfix 模块在主机上安装更新或修补程序。模块 win_updates 用于按类别安装多个更新,而 win_hotfix 可用于安装已在本地下载的单个更新或修补程序文件。

注意

win_hotfix 模块要求存在 DISM PowerShell cmdlet。这些 cmdlet 仅在 Windows Server 2012 和更高版本上默认添加,并且必须安装在较旧的 Windows 主机上。

以下示例显示了如何使用 win_updates

- name: Install all critical and security updates
  win_updates:
    category_names:
    - CriticalUpdates
    - SecurityUpdates
    state: installed
  register: update_result

- name: Reboot host if required
  win_reboot:
  when: update_result.reboot_required

以下示例显示了如何使用 win_hotfix 安装单个更新或修补程序

- name: Download KB3172729 for Server 2012 R2
  win_get_url:
    url: http://download.windowsupdate.com/d/msdownload/update/software/secu/2016/07/windows8.1-kb3172729-x64_e8003822a7ef4705cbb65623b72fd3cec73fe222.msu
    dest: C:\temp\KB3172729.msu

- name: Install hotfix
  win_hotfix:
    hotfix_kb: KB3172729
    source: C:\temp\KB3172729.msu
    state: present
  register: hotfix_result

- name: Reboot host if required
  win_reboot:
  when: hotfix_result.reboot_required

设置用户和组

Ansible 可用于在本地和域上创建 Windows 用户和组。

本地

模块 win_userwin_groupwin_group_membership 在本地管理 Windows 用户、组和组成员资格。

以下是一个创建本地帐户和组的示例,这些帐户和组可以访问同一主机上的文件夹

- name: Create local group to contain new users
  win_group:
    name: LocalGroup
    description: Allow access to C:\Development folder

- name: Create local user
  win_user:
    name: '{{ item.name }}'
    password: '{{ item.password }}'
    groups: LocalGroup
    update_password: false
    password_never_expires: true
  loop:
  - name: User1
    password: Password1
  - name: User2
    password: Password2

- name: Create Development folder
  win_file:
    path: C:\Development
    state: directory

- name: Set ACL of Development folder
  win_acl:
    path: C:\Development
    rights: FullControl
    state: present
    type: allow
    user: LocalGroup

- name: Remove parent inheritance of Development folder
  win_acl_inheritance:
    path: C:\Development
    reorganize: true
    state: absent

模块 win_domain_userwin_domain_group 管理域中的用户和组。以下是确保创建一批域用户的示例

- name: Ensure each account is created
  win_domain_user:
    name: '{{ item.name }}'
    upn: '{{ item.name }}@MY.DOMAIN.COM'
    password: '{{ item.password }}'
    password_never_expires: false
    groups:
    - Test User
    - Application
    company: Ansible
    update_password: on_create
  loop:
  - name: Test User
    password: Password
  - name: Admin User
    password: SuperSecretPass01
  - name: Dev User
    password: '@fvr3IbFBujSRh!3hBg%wgFucD8^x8W5'

运行命令

在没有适合任务的模块可用时,可以使用 win_shellwin_commandrawscript 模块运行命令或脚本。

raw 模块只是远程执行 Powershell 命令。由于 raw 没有 Ansible 通常使用的任何包装器,因此 becomeasync 和环境变量不起作用。

script 模块在一个或多个 Windows 主机上从 Ansible 控制节点执行脚本。与 raw 类似,script 当前不支持 becomeasync 或环境变量。

win_command 模块用于执行命令,该命令可以是可执行文件或批处理文件,而 win_shell 模块用于在 shell 中执行命令。

选择 Command 或 Shell

win_shellwin_command 模块都可以用于执行一个或多个命令。win_shell 模块在类似 shell 的进程(如 PowerShellcmd)中运行,因此它可以访问 shell 运算符,例如 <>|;&&||。多行命令也可以在 win_shell 中运行。

win_command 模块只是在 shell 之外运行一个进程。它仍然可以通过将 shell 命令传递给类似 cmd.exePowerShell.exe 的 shell 可执行文件来运行 shell 命令,例如 mkdirNew-Item

以下是一些使用 win_commandwin_shell 的示例

- name: Run a command under PowerShell
  win_shell: Get-Service -Name service | Stop-Service

- name: Run a command under cmd
  win_shell: mkdir C:\temp
  args:
    executable: cmd.exe

- name: Run a multiple shell commands
  win_shell: |
    New-Item -Path C:\temp -ItemType Directory
    Remove-Item -Path C:\temp -Force -Recurse
    $path_info = Get-Item -Path C:\temp
    $path_info.FullName

- name: Run an executable using win_command
  win_command: whoami.exe

- name: Run a cmd command
  win_command: cmd.exe /c mkdir C:\temp

- name: Run a vbs script
  win_command: cscript.exe script.vbs

注意

某些命令(如 mkdirdelcopy)仅存在于 CMD shell 中。要使用 win_command 运行它们,必须以 cmd.exe /c 作为前缀。

参数规则

通过 win_command 运行命令时,适用标准的 Windows 参数规则

  • 每个参数都用空格分隔,空格可以是空格或制表符。

  • 参数可以用双引号 " 包围。引号内的任何内容都被解释为单个参数,即使它包含空格。

  • 以反斜杠 \ 开头的双引号被解释为只是一个双引号 ",而不是参数分隔符。

  • 反斜杠会被按字面意思解释,除非它紧跟在双引号之前;例如 \ == \\" == "

  • 如果偶数个反斜杠后跟一个双引号,则每对反斜杠在参数中使用一个反斜杠,并且双引号用作参数的字符串分隔符。

  • 如果奇数个反斜杠后跟一个双引号,则每对反斜杠在参数中使用一个反斜杠,并且双引号被转义,并在参数中成为字面双引号。

记住这些规则,以下是一些关于引号的示例

- win_command: C:\temp\executable.exe argument1 "argument 2" "C:\path\with space" "double \"quoted\""

argv[0] = C:\temp\executable.exe
argv[1] = argument1
argv[2] = argument 2
argv[3] = C:\path\with space
argv[4] = double "quoted"

- win_command: '"C:\Program Files\Program\program.exe" "escaped \\\" backslash" unquoted-end-backslash\'

argv[0] = C:\Program Files\Program\program.exe
argv[1] = escaped \" backslash
argv[2] = unquoted-end-backslash\

# Due to YAML and Ansible parsing '\"' must be written as '{% raw %}\\{% endraw %}"'
- win_command: C:\temp\executable.exe C:\no\space\path "arg with end \ before end quote{% raw %}\\{% endraw %}"

argv[0] = C:\temp\executable.exe
argv[1] = C:\no\space\path
argv[2] = arg with end \ before end quote\"

更多信息,请参阅 转义参数

创建和运行计划任务

WinRM 有一些限制,会在运行某些命令时导致错误。绕过这些限制的一种方法是通过计划任务运行命令。计划任务是一个 Windows 组件,它提供在计划时间和不同帐户下运行可执行文件的能力。

Ansible 2.5 版本添加了一些模块,使在 Windows 中处理计划任务更容易。以下示例演示了如何将脚本作为计划任务运行,并在运行后删除自身

- name: Create scheduled task to run a process
  win_scheduled_task:
    name: adhoc-task
    username: SYSTEM
    actions:
    - path: PowerShell.exe
      arguments: |
        Start-Sleep -Seconds 30  # This isn't required, just here as a demonstration
        New-Item -Path C:\temp\test -ItemType Directory
    # Remove this action if the task shouldn't be deleted on completion
    - path: cmd.exe
      arguments: /c schtasks.exe /Delete /TN "adhoc-task" /F
    triggers:
    - type: registration

- name: Wait for the scheduled task to complete
  win_scheduled_task_stat:
    name: adhoc-task
  register: task_stat
  until: (task_stat.state is defined and task_stat.state.status != "TASK_STATE_RUNNING") or (task_stat.task_exists == False)
  retries: 12
  delay: 10

注意

以上示例中使用的模块在 Ansible 2.5 版本中进行了更新/添加。

Windows 的路径格式

Windows 在许多方面与传统的 POSIX 操作系统不同。其中一个主要变化是将路径分隔符从 / 更改为 \。这可能会导致剧本编写方式出现重大问题,因为 \ 通常在 POSIX 系统中用作转义字符。

Ansible 允许两种不同的语法样式;每种样式处理 Windows 的路径分隔符的方式都不同

YAML 样式

当使用 YAML 语法编写任务时,规则由 YAML 标准明确定义

  • 当使用普通字符串(不带引号)时,YAML 不会将反斜杠视为转义字符。

  • 当使用单引号 ' 时,YAML 不会将反斜杠视为转义字符。

  • 当使用双引号 " 时,反斜杠被视为转义字符,需要用另一个反斜杠进行转义。

注意

只有在绝对必要或 YAML 要求时才应该使用引号引住字符串,并且使用单引号。

YAML 规范考虑了以下 转义序列

  • \0, \\, \", \_, \a, \b, \e, \f, \n, \r, \t, \v, \L, \N\P – 单字符转义

  • <TAB>, <SPACE>, <NBSP>, <LNSP>, <PSP> – 特殊字符

  • \x.. – 2 位十六进制转义

  • \u.... – 4 位十六进制转义

  • \U........ – 8 位十六进制转义

以下是一些关于如何编写 Windows 路径的示例

# GOOD
tempdir: C:\Windows\Temp

# WORKS
tempdir: 'C:\Windows\Temp'
tempdir: "C:\\Windows\\Temp"

# BAD, BUT SOMETIMES WORKS
tempdir: C:\\Windows\\Temp
tempdir: 'C:\\Windows\\Temp'
tempdir: C:/Windows/Temp

这是一个将会失败的示例

# FAILS
tempdir: "C:\Windows\Temp"

此示例演示了在需要时使用单引号

---
- name: Copy tomcat config
  win_copy:
    src: log4j.xml
    dest: '{{tc_home}}\lib\log4j.xml'

旧的 key=value 样式

旧的 key=value 语法用于命令行上的临时命令,或在剧本内部使用。不鼓励在剧本中使用这种样式,因为反斜杠字符需要被转义,这使得剧本更难阅读。旧的语法取决于 Ansible 中的特定实现,并且引号(单引号和双引号)对 Ansible 如何解析没有任何影响。

Ansible key=value 解析器 parse_kv() 考虑以下转义序列

  • \, ', ", \a, \b, \f, \n, \r, \t\v – 单字符转义

  • \x.. – 2 位十六进制转义

  • \u.... – 4 位十六进制转义

  • \U........ – 8 位十六进制转义

  • \N{...} – 按名称查找 Unicode 字符

这意味着反斜杠是某些序列的转义字符,因此在这种形式下通常更安全地转义反斜杠。

以下是一些关于使用 key=value 样式处理 Windows 路径的示例

# GOOD
tempdir=C:\\Windows\\Temp

# WORKS
tempdir='C:\\Windows\\Temp'
tempdir="C:\\Windows\\Temp"

# BAD, BUT SOMETIMES WORKS
tempdir=C:\Windows\Temp
tempdir='C:\Windows\Temp'
tempdir="C:\Windows\Temp"
tempdir=C:/Windows/Temp

# FAILS
tempdir=C:\Windows\temp
tempdir='C:\Windows\temp'
tempdir="C:\Windows\temp"

失败的示例不会直接失败,而是会将 \t 替换为 <TAB> 字符,导致 tempdir 变为 C:\Windows<TAB>emp

限制

您无法使用 Ansible 和 Windows 执行以下操作

  • 升级 PowerShell

  • 与 WinRM 监听器交互

由于 WinRM 依赖于服务在正常操作期间保持在线和运行,因此您无法使用 Ansible 升级 PowerShell 或与 WinRM 监听器交互。这两种操作都会导致连接失败。从技术上讲,可以使用 async 或计划任务来避免这种情况,但如果它运行的进程破坏了 Ansible 使用的基础连接,则这些方法会很脆弱,最好将其留给引导过程或在创建映像之前进行。

开发 Windows 模块

因为用于 Windows 的 Ansible 模块是用 PowerShell 编写的,所以 Windows 模块的开发指南与标准模块的开发指南大不相同。请参阅 Windows 模块开发演练 获取更多信息。

另请参阅

Ansible 剧本

剧本简介

Ansible 提示和技巧

剧本的技巧和窍门

Windows 模块列表

Windows 特定模块列表,全部使用 PowerShell 实现

交流

有疑问?需要帮助?想分享您的想法?请访问 Ansible 交流指南