Using Ansible and Windows

    Ansible can be used to orchestrate a multitude of tasks on Windows servers.Below are some examples and info about common tasks.

    There are three main ways that Ansible can be used to install software:

    • Using the module. This sources the program data from the defaultpublic Chocolatey repository. Internal repositories canbe used instead by setting the source option.
    • Using the win_package module. This installs software using an MSI or .exe installerfrom 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.

    Below are some examples of using all three options to install 7-Zip:

    Some installers like Microsoft Office or SQL Server require credential delegation oraccess to components restricted by WinRM. The best method to bypass theseissues is to use become with the task. With become, Ansible will runthe 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 updatesor hotfixes on a host. The module win_updates is used to install multipleupdates by category, while win_hotfix can be used to install a singleupdate or hotfix file that has been downloaded locally.

    Note

    The win_hotfix module has a requirement that the DISM PowerShell cmdlets arepresent. These cmdlets were only added by default on Windows Server 2012and newer and must be installed on older Windows hosts.

    The following example shows how win_updates can be used:

    1. - name: install all critical and security updates
    2. win_updates:
    3. category_names:
    4. - CriticalUpdates
    5. - SecurityUpdates
    6. state: installed
    7. register: update_result
    8.  
    9. - name: reboot host if required
    10. win_reboot:
    11. when: update_result.reboot_required

    The following example show how win_hotfix can be used to install a singleupdate or hotfix:

    1. - name: download KB3172729 for Server 2012 R2
    2. win_get_url:
    3. url: http://download.windowsupdate.com/d/msdownload/update/software/secu/2016/07/windows8.1-kb3172729-x64_e8003822a7ef4705cbb65623b72fd3cec73fe222.msu
    4. dest: C:\temp\KB3172729.msu
    5.  
    6. - name: install hotfix
    7. win_hotfix:
    8. hotfix_kb: KB3172729
    9. source: C:\temp\KB3172729.msu
    10. state: present
    11. register: hotfix_result
    12.  
    13. - name: reboot host if required
    14. win_reboot:
    15. when: hotfix_result.reboot_required

    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 manageWindows users, groups and group memberships locally.

    1. - name: create local group to contain new users
    2. win_group:
    3. name: LocalGroup
    4. description: Allow access to C:\Development folder
    5.  
    6. - name: create local user
    7. win_user:
    8. name: '{{item.name}}'
    9. password: '{{item.password}}'
    10. groups: LocalGroup
    11. update_password: no
    12. password_never_expired: yes
    13. with_items:
    14. - name: User1
    15. password: Password1
    16. - name: User2
    17. password: Password2
    18.  
    19. - name: create Development folder
    20. win_file:
    21. path: C:\Development
    22. state: directory
    23. - name: set ACL of Development folder
    24. win_acl:
    25. path: C:\Development
    26. rights: FullControl
    27. state: present
    28. type: allow
    29. user: LocalGroup
    30.  
    31. - name: remove parent inheritance of Development folder
    32. win_acl_inheritance:
    33. path: C:\Development
    34. reorganize: yes
    35. state: absent

    Domain

    The modules win_domain_user and win_domain_group manages users andgroups in a domain. The below is an example of ensuring a batch of domain usersare created:

    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 rawhas none of the wrappers that Ansible typically uses, become, asyncand environment variables do not work.

    The script module executes a script from the Ansible controller onone or more Windows hosts. Like raw, script currently does not supportbecome, async, or environment variables.

    The win_command module is used to execute a command which is either anexecutable 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 or cmd, so it has access to shelloperators 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 stillrun a shell command like mkdir or New-Item by passing the shell commandsto a shell executable like cmd.exe or PowerShell.exe.

    Here are some examples of using win_command and win_shell:

    1. - name: run a command under PowerShell
    2. win_shell: Get-Service -Name service | Stop-Service
    3.  
    4. - name: run a command under cmd
    5. win_shell: mkdir C:\temp
    6. args:
    7. executable: cmd.exe
    8.  
    9. - name: run a multiple shell commands
    10. win_shell: |
    11. New-Item -Path C:\temp -ItemType Directory
    12. Remove-Item -Path C:\temp -Force -Recurse
    13. $path_info = Get-Item -Path C:\temp
    14. $path_info.FullName
    15.  
    16. - name: run an executable using win_command
    17. win_command: whoami.exe
    18.  
    19. - name: run a cmd command
    20. win_command: cmd.exe /c mkdir C:\temp
    21.  
    22. - name: run a vbs script
    23. win_command: cscript.exe script.vbs

    Note

    Some commands like mkdir, del, and copy only exist inthe CMD shell. To run them with win_command they must beprefixed with cmd.exe /c.

    Argument Rules

    When running a command through win_command, the standard Windows argumentrules apply:

    • Each argument is delimited by a white space, which can either be a space or atab.
    • An argument can be surrounded by double quotes ". Anything inside thesequotes is interpreted as a single argument even if it contains whitespace.
    • A double quote preceded by a backslash \ is interpreted as just a doublequote " and not as an argument delimiter.
    • Backslashes are interpreted literally unless it immediately preceeds doublequotes; for example \ == \ and \" == "
    • If an even number of backslashes is followed by a double quote, onebackslash is used in the argument for every pair, and the double quote isused as a string delimiter for the argument.
    • If an odd number of backslashes is followed by a double quote, one backslashis used in the argument for every pair, and the double quote is escaped andmade a literal double quote in the argument.

    With those rules in mind, here are some examples of quoting:

    1. - win_command: C:\temp\executable.exe argument1 "argument 2" "C:\path\with space" "double \"quoted\""
    2.  
    3. argv[0] = C:\temp\executable.exe
    4. argv[1] = argument1
    5. argv[2] = argument 2
    6. argv[3] = C:\path\with space
    7. argv[4] = double "quoted"
    8.  
    9. - win_command: '"C:\Program Files\Program\program.exe" "escaped \\\" backslash" unqouted-end-backslash\'
    10.  
    11. argv[0] = C:\Program Files\Program\program.exe
    12. argv[1] = escaped \" backslash
    13. argv[2] = unquoted-end-backslash\
    14.  
    15. # due to YAML and Ansible parsing '\"' must be written as '{% raw %}\\{% endraw %}"'
    16. - win_command: C:\temp\executable.exe C:\no\space\path "arg with end \ before end quote{% raw %}\\{% endraw %}"
    17.  
    18. argv[0] = C:\temp\executable.exe
    19. argv[1] = C:\no\space\path
    20. argv[2] = arg with end \ before end quote\"

    For more information, see .aspx).

    WinRM has some restrictions in place that cause errors when running certaincommands. One way to bypass these restrictions is to run a command through ascheduled task. A scheduled task is a Windows component that provides theability 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 afterrunning:

    1. - name: create scheduled task to run a process
    2. win_scheduled_task:
    3. name: adhoc-task
    4. username: SYSTEM
    5. actions:
    6. arguments: |
    7. Start-Sleep -Seconds 30 # this isn't required, just here as a demonstration
    8. New-Item -Path C:\temp\test -ItemType Directory
    9. # remove this action if the task shouldn't be deleted on completion
    10. - path: cmd.exe
    11. arguments: /c schtasks.exe /Delete /TN "adhoc-task" /F
    12. triggers:
    13. - type: registration
    14.  
    15. - name: wait for the scheduled task to complete
    16. win_scheduled_task_stat:
    17. name: adhoc-task
    18. register: task_stat
    19. until: (task_stat.state is defined and task_stat.state.status != "TASK_STATE_RUNNING") or (task_stat.task_exists == False)
    20. retries: 12
    21. delay: 10

    Note

    Windows differs from a traditional POSIX operating system in many ways. One ofthe major changes is the shift from / as the path separator to \. Thiscan cause major issues with how playbooks are written, since \ is often usedas 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 YAMLstandard:

    • When using a normal string (without quotes), YAML will not consider thebackslash an escape character.
    • When using single quotes ', YAML will not consider the backslash anescape character.
    • When using double quotes ", the backslash is considered an escapecharacter and needs to escaped with another backslash.

    Note

    You should only quote strings when it is absolutelynecessary or required by YAML, and then use single quotes.

    The YAML specification considers the following escape sequences:

    • \0, \, \", _, , \b, \e, \f, \n, \r, \t,\v, \L, \N and \P – Single character escape
    • <TAB>, <SPACE>, <NBSP>, <LNSP>, <PSP> – Specialcharacters
    • \x.. – 2-digit hex escape
    • \u…. – 4-digit hex escape
    • \U…….. – 8-digit hex escape

    Here are some examples on how to write Windows paths:

    This is an example which will fail:

    1. # FAILS
    2. tempdir: "C:\Windows\Temp"

    This example shows the use of single quotes when they are required:

    1. ---
    2. - name: Copy tomcat config
    3. win_copy:
    4. src: log4j.xml
    5. dest: '{{tc_home}}\lib\log4j.xml'

    The legacy key=value syntax is used on the command line for adhoc commands,or inside playbooks. The use of this style is discouraged within playbooksbecause 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 byAnsible.

    The Ansible key=value parser parse_kv() considers the following escapesequences:

    • \, ', ", \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 itis usually safer to escape a backslash when in this form.

    Here are some examples of using Windows paths with the key=value style:

    1. GOOD
    2. tempdir=C:\\Windows\\Temp
    3.  
    4. WORKS
    5. tempdir='C:\\Windows\\Temp'
    6. tempdir="C:\\Windows\\Temp"
    7.  
    8. BAD, BUT SOMETIMES WORKS
    9. tempdir=C:\Windows\Temp
    10. tempdir='C:\Windows\Temp'
    11. tempdir="C:\Windows\Temp"
    12. tempdir=C:/Windows/Temp
    13.  
    14. FAILS
    15. tempdir=C:\Windows\temp
    16. tempdir='C:\Windows\temp'
    17. 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.

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

    See also