2017-10-13 03:40:47 +02:00
Using Ansible and Windows
=========================
When using Ansible to manage Windows, many of the syntax and rules that apply
for Unix/Linux hosts also apply to Windows, but there are still some differences
when it comes to components like path separators and OS-specific tasks.
This document covers details specific to using Ansible for Windows.
.. contents :: Topics
2018-09-13 19:38:49 +02:00
:local:
2017-10-13 03:40:47 +02:00
Use Cases
`` ` ` ` ` ` ``
Ansible can be used to orchestrate a multitude of tasks on Windows servers.
Below are some examples and info about common tasks.
Installing Software
-------------------
There are three main ways that Ansible can be used to install software:
* Using the `` win_chocolatey `` module. This sources the program data from the default
public `Chocolatey <https://chocolatey.org/> `_ repository. Internal repositories can
be used instead by setting the `` source `` option.
* Using the `` win_package `` module. This installs software using an MSI or .exe installer
from a local/network path or URL.
* Using the `` win_command `` or `` win_shell `` module to run an installer manually.
The `` win_chocolatey `` module is recommended since it has the most complete logic for checking to see if a package has already been installed and is up-to-date.
2018-11-15 22:17:29 +01:00
Below are some examples of using all three options to install 7-Zip:
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
.. code-block :: yaml+jinja
# Install/uninstall with chocolatey
- name: Ensure 7-Zip is installed via Chocolatey
2017-10-13 03:40:47 +02:00
win_chocolatey:
name: 7zip
state: present
2018-11-15 22:17:29 +01:00
- name: Ensure 7-Zip is not installed via Chocolatey
2017-10-13 03:40:47 +02:00
win_chocolatey:
name: 7zip
state: absent
2018-11-15 22:17:29 +01:00
# Install/uninstall with win_package
- name: Download the 7-Zip package
2017-10-13 03:40:47 +02:00
win_get_url:
2018-07-21 15:48:47 +02:00
url: https://www.7-zip.org/a/7z1701-x64.msi
2017-10-13 03:40:47 +02:00
dest: C:\temp\7z.msi
2018-11-15 22:17:29 +01:00
- name: Ensure 7-Zip is installed via win_package
2017-10-13 03:40:47 +02:00
win_package:
path: C:\temp\7z.msi
state: present
2018-11-15 22:17:29 +01:00
- name: Ensure 7-Zip is not installed via win_package
2017-10-13 03:40:47 +02:00
win_package:
path: C:\temp\7z.msi
state: absent
2018-11-15 22:17:29 +01:00
# Install/uninstall with win_command
- name: Download the 7-Zip package
2017-10-13 03:40:47 +02:00
win_get_url:
2018-07-21 15:48:47 +02:00
url: https://www.7-zip.org/a/7z1701-x64.msi
2017-10-13 03:40:47 +02:00
dest: C:\temp\7z.msi
2018-11-15 22:17:29 +01:00
- name: Check if 7-Zip is already installed
2017-10-13 03:40:47 +02:00
win_reg_stat:
name: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{23170F69-40C1-2702-1701-000001000000}
register: 7zip_installed
2018-11-15 22:17:29 +01:00
- name: Ensure 7-Zip is installed via win_command
2017-10-13 03:40:47 +02:00
win_command: C:\Windows\System32\msiexec.exe /i C:\temp\7z.msi /qn /norestart
2018-11-15 22:17:29 +01:00
when: 7zip_installed.exists == false
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
- name: Ensure 7-Zip is uninstalled via win_command
2017-10-13 03:40:47 +02:00
win_command: C:\Windows\System32\msiexec.exe /x {23170F69-40C1-2702-1701-000001000000} /qn /norestart
2018-11-15 22:17:29 +01:00
when: 7zip_installed.exists == true
2017-10-13 03:40:47 +02:00
Some installers like Microsoft Office or SQL Server require credential delegation or
access to components restricted by WinRM. The best method to bypass these
issues is to use `` become `` with the task. With `` become `` , Ansible will run
the installer as if it were run interactively on the host.
.. Note :: Many installers do not properly pass back error information over WinRM. In these cases, if the install has been verified to work locally the recommended method is to use become.
.. Note :: Some installers restart the WinRM or HTTP services, or cause them to become temporarily unavailable, making Ansible assume the system is unreachable.
Installing Updates
------------------
The `` win_updates `` and `` win_hotfix `` modules can be used to install updates
or hotfixes on a host. The module `` win_updates `` is used to install multiple
updates by category, while `` win_hotfix `` can be used to install a single
update or hotfix file that has been downloaded locally.
.. Note :: The `` win_hotfix `` module has a requirement that the DISM PowerShell cmdlets are
present. These cmdlets were only added by default on Windows Server 2012
and newer and must be installed on older Windows hosts.
2018-11-15 22:17:29 +01:00
The following example shows how `` win_updates `` can be used:
.. code-block :: yaml+jinja
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
- name: Install all critical and security updates
2017-10-13 03:40:47 +02:00
win_updates:
category_names:
- CriticalUpdates
- SecurityUpdates
state: installed
register: update_result
2018-11-15 22:17:29 +01:00
- name: Reboot host if required
2017-10-13 03:40:47 +02:00
win_reboot:
when: update_result.reboot_required
The following example show how `` win_hotfix `` can be used to install a single
2018-11-15 22:17:29 +01:00
update or hotfix:
.. code-block :: yaml+jinja
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
- name: Download KB3172729 for Server 2012 R2
2017-10-13 03:40:47 +02:00
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
2018-11-15 22:17:29 +01:00
- name: Install hotfix
2017-10-13 03:40:47 +02:00
win_hotfix:
hotfix_kb: KB3172729
source: C:\temp\KB3172729.msu
state: present
register: hotfix_result
2018-11-15 22:17:29 +01:00
- name: Reboot host if required
2017-10-13 03:40:47 +02:00
win_reboot:
when: hotfix_result.reboot_required
Set Up Users and Groups
-----------------------
Ansible can be used to create Windows users and groups both locally and on a domain.
Local
+++++
The modules `` win_user `` , `` win_group `` and `` win_group_membership `` manage
Windows users, groups and group memberships locally.
The following is an example of creating local accounts and groups that can
2018-11-15 22:17:29 +01:00
access a folder on the same host:
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
.. code-block :: yaml+jinja
- name: Create local group to contain new users
2017-10-13 03:40:47 +02:00
win_group:
name: LocalGroup
description: Allow access to C:\Development folder
2018-11-15 22:17:29 +01:00
- name: Create local user
2017-10-13 03:40:47 +02:00
win_user:
2018-11-15 22:17:29 +01:00
name: '{{ item.name }}'
password: '{{ item.password }}'
2017-10-13 03:40:47 +02:00
groups: LocalGroup
update_password: no
password_never_expired: yes
2018-10-16 22:54:09 +02:00
loop:
2017-10-13 03:40:47 +02:00
- name: User1
password: Password1
- name: User2
password: Password2
2018-11-15 22:17:29 +01:00
- name: Create Development folder
2017-10-13 03:40:47 +02:00
win_file:
path: C:\Development
state: directory
2018-11-15 22:17:29 +01:00
- name: Set ACL of Development folder
2017-10-13 03:40:47 +02:00
win_acl:
path: C:\Development
rights: FullControl
state: present
type: allow
user: LocalGroup
2018-11-15 22:17:29 +01:00
- name: Remove parent inheritance of Development folder
2017-10-13 03:40:47 +02:00
win_acl_inheritance:
path: C:\Development
reorganize: yes
state: absent
Domain
++++++
The modules `` win_domain_user `` and `` win_domain_group `` manages users and
groups in a domain. The below is an example of ensuring a batch of domain users
2018-11-15 22:17:29 +01:00
are created:
.. code-block :: yaml+jinja
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
- name: Ensure each account is created
2017-10-13 03:40:47 +02:00
win_domain_user:
2018-11-15 22:17:29 +01:00
name: '{{ item.name }}'
upn: '{{ item.name }}@MY.DOMAIN.COM'
password: '{{ item.password }}'
2017-10-13 03:40:47 +02:00
password_never_expires: no
groups:
- Test User
- Application
company: Ansible
update_password: on_create
2018-10-16 22:54:09 +02:00
loop:
2017-10-13 03:40:47 +02:00
- name: Test User
password: Password
- name: Admin User
password: SuperSecretPass01
- name: Dev User
password: '@fvr3IbFBujSRh!3hBg%wgFucD8^x8W5'
Running Commands
----------------
In cases where there is no appropriate module available for a task,
a command or script can be run using the `` win_shell `` , `` win_command `` , `` raw `` , and `` script `` modules.
The `` raw `` module simply executes a Powershell command remotely. Since `` raw ``
has none of the wrappers that Ansible typically uses, `` become `` , `` async ``
and environment variables do not work.
The `` script `` module executes a script from the Ansible controller on
one or more Windows hosts. Like `` raw `` , `` script `` currently does not support
`` become `` , `` async `` , or environment variables.
The `` win_command `` module is used to execute a command which is either an
executable or batch file, while the `` win_shell `` module is used to execute commands within a shell.
Choosing Command or Shell
+++++++++++++++++++++++++
The `` win_shell `` and `` win_command `` modules can both be used to execute a command or commands.
The `` win_shell `` module is run within a shell-like process like `` PowerShell `` or `` cmd `` , so it has access to shell
operators like `` < `` , `` > `` , `` | `` , `` ; `` , `` && `` , and `` || `` . Multi-lined commands can also be run in `` win_shell `` .
The `` win_command `` module simply runs a process outside of a shell. It can still
run a shell command like `` mkdir `` or `` New-Item `` by passing the shell commands
to a shell executable like `` cmd.exe `` or `` PowerShell.exe `` .
2018-11-15 22:17:29 +01:00
Here are some examples of using `` win_command `` and `` win_shell `` :
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
.. code-block :: yaml+jinja
- name: Run a command under PowerShell
2017-10-13 03:40:47 +02:00
win_shell: Get-Service -Name service | Stop-Service
2018-11-15 22:17:29 +01:00
- name: Run a command under cmd
2017-10-13 03:40:47 +02:00
win_shell: mkdir C:\temp
args:
executable: cmd.exe
2018-11-15 22:17:29 +01:00
- name: Run a multiple shell commands
2017-10-13 03:40:47 +02:00
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
2018-11-15 22:17:29 +01:00
- name: Run an executable using win_command
2017-10-13 03:40:47 +02:00
win_command: whoami.exe
2018-11-15 22:17:29 +01:00
- name: Run a cmd command
2017-10-13 03:40:47 +02:00
win_command: cmd.exe /c mkdir C:\temp
2018-11-15 22:17:29 +01:00
- name: Run a vbs script
2017-10-13 03:40:47 +02:00
win_command: cscript.exe script.vbs
.. Note :: Some commands like `` mkdir `` , `` del `` , and `` copy `` only exist in
the CMD shell. To run them with `` win_command `` they must be
prefixed with `` cmd.exe /c `` .
Argument Rules
++++++++++++++
When running a command through `` win_command `` , the standard Windows argument
rules apply:
* Each argument is delimited by a white space, which can either be a space or a
tab.
* An argument can be surrounded by double quotes `` " `` . Anything inside these
quotes is interpreted as a single argument even if it contains whitespace.
* A double quote preceded by a backslash `` \ `` is interpreted as just a double
quote `` " `` and not as an argument delimiter.
2018-10-24 18:01:24 +02:00
* Backslashes are interpreted literally unless it immediately precedes double
2017-10-13 03:40:47 +02:00
quotes; for example `` \ `` == `` \ `` and `` \" `` == `` " ``
* If an even number of backslashes is followed by a double quote, one
backslash is used in the argument for every pair, and the double quote is
used as a string delimiter for the argument.
* If an odd number of backslashes is followed by a double quote, one backslash
is used in the argument for every pair, and the double quote is escaped and
made a literal double quote in the argument.
2018-11-15 22:17:29 +01:00
With those rules in mind, here are some examples of quoting:
.. code-block :: yaml+jinja
2017-10-13 03:40:47 +02:00
- 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"
2018-10-24 18:01:24 +02:00
- win_command: '"C:\Program Files\Program\program.exe" "escaped \\\" backslash" unquoted-end-backslash\'
2017-10-13 03:40:47 +02:00
argv[0] = C:\Program Files\Program\program.exe
argv[1] = escaped \" backslash
argv[2] = unquoted-end-backslash\
2018-11-15 22:17:29 +01:00
# Due to YAML and Ansible parsing '\"' must be written as '{% raw %}\\{% endraw %}"'
2017-12-17 21:26:04 +01:00
- win_command: C:\temp\executable.exe C:\no\space\path "arg with end \ before end quote{% raw %}\\{% endraw %}"
2017-10-13 03:40:47 +02:00
argv[0] = C:\temp\executable.exe
argv[1] = C:\no\space\path
argv[2] = arg with end \ before end quote\"
For more information, see `escaping arguments <https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx> `_ .
Creating and Running a Scheduled Task
-------------------------------------
WinRM has some restrictions in place that cause errors when running certain
commands. One way to bypass these restrictions is to run a command through a
scheduled task. A scheduled task is a Windows component that provides the
ability to run an executable on a schedule and under a different account.
Ansible version 2.5 added modules that make it easier to work with scheduled tasks in Windows.
The following is an example of running a script as a scheduled task that deletes itself after
2018-11-15 22:17:29 +01:00
running:
.. code-block :: yaml+jinja
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
- name: Create scheduled task to run a process
2017-10-13 03:40:47 +02:00
win_scheduled_task:
name: adhoc-task
username: SYSTEM
actions:
- path: PowerShell.exe
arguments: |
2018-11-15 22:17:29 +01:00
Start-Sleep -Seconds 30 # This isn't required, just here as a demonstration
2017-10-13 03:40:47 +02:00
New-Item -Path C:\temp\test -ItemType Directory
2018-11-15 22:17:29 +01:00
# Remove this action if the task shouldn't be deleted on completion
2017-10-13 03:40:47 +02:00
- path: cmd.exe
arguments: /c schtasks.exe /Delete /TN "adhoc-task" /F
triggers:
- type: registration
2018-11-15 22:17:29 +01:00
- name: Wait for the scheduled task to complete
2017-10-13 03:40:47 +02:00
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
.. Note :: The modules used in the above example were updated/added in Ansible
version 2.5.
Path Formatting for Windows
`` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ``
Windows differs from a traditional POSIX operating system in many ways. One of
the major changes is the shift from `` / `` as the path separator to `` \ `` . This
can cause major issues with how playbooks are written, since `` \ `` is often used
as an escape character on POSIX systems.
Ansible allows two different styles of syntax; each deals with path separators for Windows differently:
YAML Style
----------
When using the YAML syntax for tasks, the rules are well-defined by the YAML
standard:
* When using a normal string (without quotes), YAML will not consider the
backslash an escape character.
* When using single quotes `` ' `` , YAML will not consider the backslash an
escape character.
* When using double quotes `` " `` , the backslash is considered an escape
character and needs to escaped with another backslash.
.. Note :: You should only quote strings when it is absolutely
necessary or required by YAML, and then use single quotes.
2018-08-13 21:54:14 +02:00
The YAML specification considers the following `escape sequences <http://yaml.org/spec/current.html#id2517668> `_ :
2017-10-13 03:40:47 +02:00
2018-03-15 21:06:16 +01:00
* `` \0 `` , `` \\ `` , `` \" `` , `` \_ `` , `` \a `` , `` \b `` , `` \e `` , `` \f `` , `` \n `` , `` \r `` , `` \t `` ,
`` \v `` , `` \L `` , `` \N `` and `` \P `` -- Single character escape
2017-10-13 03:40:47 +02:00
* `` <TAB> `` , `` <SPACE> `` , `` <NBSP> `` , `` <LNSP> `` , `` <PSP> `` -- Special
characters
* `` \x.. `` -- 2-digit hex escape
* `` \u.... `` -- 4-digit hex escape
* `` \U........ `` -- 8-digit hex escape
2018-11-15 22:17:29 +01:00
Here are some examples on how to write Windows paths:
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
.. code-block :: yaml+jinja
# GOOD
2017-10-13 03:40:47 +02:00
tempdir: C:\Windows\Temp
2018-11-15 22:17:29 +01:00
# WORKS
2017-10-13 03:40:47 +02:00
tempdir: 'C:\Windows\Temp'
tempdir: "C:\\Windows\\Temp"
2018-11-15 22:17:29 +01:00
# BAD, BUT SOMETIMES WORKS
2017-10-13 03:40:47 +02:00
tempdir: C:\\Windows\\Temp
tempdir: 'C:\\Windows\\Temp'
tempdir: C:/Windows/Temp
2018-11-15 22:17:29 +01:00
# FAILS
2017-10-13 03:40:47 +02:00
tempdir: "C:\Windows\Temp"
---
2018-11-15 22:17:29 +01:00
# Example of single quotes when they are required
- name: Copy tomcat config
2017-10-13 03:40:47 +02:00
win_copy:
src: log4j.xml
dest: '{{tc_home}}\lib\log4j.xml'
Legacy key=value Style
----------------------
2018-10-24 18:01:24 +02:00
The legacy `` key=value `` syntax is used on the command line for ad-hoc commands,
2017-10-13 03:40:47 +02:00
or inside playbooks. The use of this style is discouraged within playbooks
because backslash characters need to be escaped, making playbooks harder to read.
The legacy syntax depends on the specific implementation in Ansible, and quoting
(both single and double) does not have any effect on how it is parsed by
Ansible.
The Ansible key=value parser parse_kv() considers the following escape
sequences:
* `` \ `` , `` ' `` , `` " `` , `` \a `` , `` \b `` , `` \f `` , `` \n `` , `` \r `` , `` \t `` and
`` \v `` -- Single character escape
* `` \x.. `` -- 2-digit hex escape
* `` \u.... `` -- 4-digit hex escape
* `` \U........ `` -- 8-digit hex escape
* `` \N{...} `` -- Unicode character by name
This means that the backslash is an escape character for some sequences, and it
is usually safer to escape a backslash when in this form.
2018-11-15 22:17:29 +01:00
Here are some examples of using Windows paths with the key=value style:
.. code-block :: ini
2017-10-13 03:40:47 +02:00
2018-11-15 22:17:29 +01:00
# GOOD
2017-10-13 03:40:47 +02:00
tempdir=C:\\Windows\\Temp
2018-11-15 22:17:29 +01:00
# WORKS
2017-10-13 03:40:47 +02:00
tempdir='C:\\Windows\\Temp'
tempdir="C:\\Windows\\Temp"
2018-11-15 22:17:29 +01:00
# BAD, BUT SOMETIMES WORKS
2017-10-13 03:40:47 +02:00
tempdir=C:\Windows\Temp
tempdir='C:\Windows\Temp'
tempdir="C:\Windows\Temp"
tempdir=C:/Windows/Temp
2018-11-15 22:17:29 +01:00
# FAILS
2017-10-13 03:40:47 +02:00
tempdir=C:\Windows\temp
tempdir='C:\Windows\temp'
tempdir="C:\Windows\temp"
The failing examples don't fail outright but will substitute `` \t `` with the
`` <TAB> `` character resulting in `` tempdir `` being `` C:\Windows<TAB>emp `` .
Limitations
`` ` ` ` ` ` ` ` ``
Some things you cannot do with Ansible and Windows are:
* Upgrade PowerShell
* Interact with the WinRM listeners
Because WinRM is reliant on the services being online and running during normal operations, you cannot upgrade PowerShell or interact with WinRM listeners with Ansible. Both of these actions will cause the connection to fail. This can technically be avoided by using `` async `` or a scheduled task, but those methods are fragile if the process it runs breaks the underlying connection Ansible uses, and are best left to the bootstrapping process or before an image is
created.
Developing Windows Modules
`` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ``
Because Ansible modules for Windows are written in PowerShell, the development
guides for Windows modules differ substantially from those for standard standard modules. Please see
2018-03-14 20:44:21 +01:00
:ref: `developing_modules_general_windows` for more information.
2017-10-13 03:40:47 +02:00
.. seealso ::
:doc: `index`
The documentation index
:doc: `playbooks`
An introduction to playbooks
:doc: `playbooks_best_practices`
Best practices advice
2018-04-24 16:51:12 +02:00
:ref: `List of Windows Modules <windows_modules>`
2017-10-13 03:40:47 +02:00
Windows specific module list, all implemented in PowerShell
2018-07-21 15:48:47 +02:00
`User Mailing List <https://groups.google.com/group/ansible-project> `_
2017-10-13 03:40:47 +02:00
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net> `_
#ansible IRC chat channel