使用 Ansible 和 Windows
当使用 Ansible 管理 Windows 时,许多适用于 Unix/Linux 主机的语法和规则也适用于 Windows,但在路径分隔符和特定于操作系统的任务等组件方面仍然存在一些差异。本文档介绍了使用 Ansible 管理 Windows 的具体细节。
用例
Ansible 可用于在 Windows 服务器上编排大量任务。下面是一些示例以及有关常见任务的信息。
安装软件
Ansible 可以使用三种主要方法来安装软件
使用
win_chocolatey
模块。这会从默认的公共 Chocolatey 存储库获取程序数据。可以通过设置source
选项来使用内部存储库。使用
win_package
模块。这使用 MSI 或 .exe 安装程序从本地/网络路径或 URL 安装软件。使用
win_command
或win_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_updates
和 win_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_user
、win_group
和 win_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_user
和 win_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_shell
、win_command
、raw
和 script
模块运行命令或脚本。
raw
模块只是远程执行 Powershell 命令。由于 raw
没有 Ansible 通常使用的任何包装器,因此 become
、async
和环境变量不起作用。
script
模块在 Ansible 控制节点上执行一个脚本,并将其应用于一个或多个 Windows 主机。与 raw
一样,script
当前不支持 become
、async
或环境变量。
win_command
模块用于执行可执行文件或批处理文件,而 win_shell
模块用于在 shell 中执行命令。
选择命令或 Shell
win_shell
和 win_command
模块都可用于执行命令。 win_shell
模块在类似 shell 的进程(如 PowerShell
或 cmd
)中运行,因此它可以访问 shell 运算符(如 <
、>
、|
、;
、&&
和 ||
)。多行命令也可以在 win_shell
中运行。
win_command
模块简单地在 shell 外部运行一个进程。它仍然可以通过将 shell 命令传递给 shell 可执行文件(如 cmd.exe
或 PowerShell.exe
)来运行 shell 命令,例如 mkdir
或 New-Item
。
以下是一些使用 win_command
和 win_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
注意
一些命令,例如 mkdir
、del
和 copy
,仅存在于 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 通信指南。