07 Jun 2020

Ansible & PowerShell


sudo yum install -y python3 python3-pip
sudo alternatives --set python /usr/bin/python3
pip3 install ansible --user

Test PS-Remoting

$pwd = ConvertTo-SecureString -String 'P@@@ssword54321' -AsPlainText -Force
$psCredential = New-Object System.Management.Automation.PSCredential('jmpesp\administrator',$pwd)

Test-WSMan -ComputerName "file4.jmpesp.xyz" -Credential $psCredential -Authentication Negotiate

Configure Ansible Client on the Remote Host

$winHostSession = New-PSSession "file4.jmpesp.xyz" -Credential $psCredential

$scriptBlock = {
      $url = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"
      $file = "$env:temp\ConfigureRemotingForAnsible.ps1"
      (New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)
      & $file
      winrm set winrm/config/service '@{AllowUnencrypted="true"}'
Invoke-Command -Session $winHostSession -ScriptBlock $scriptBlock

Configure Ansible Server

Configure the hosts file:

sudo mkdir /etc/ansible && cd /etc/ansible
sudo touch hosts

Add hosts in this format:


Add configuration for windows connections to hosts in this format (windows is the group name shown in the previous example)


You might need the kerberos client

yum -y install python-devel krb5-devel krb5-libs krb5-workstation

And if you do, you’ll also need to add the python-kerberos wrapper for ansible

pip install pywinrm[kerberos]

Some things you might need if the above errors out:

sudo yum install gcc
sudo yum install python3-devel
sudo yum install krb5-devel

Test with

ansible windows -m win_ping

where “windows” is the group name from above and win_ping is an ansible module that does what you’d expect.

If it fails with:

file4= | FAILED! => {
    "msg": "winrm or requests is not installed: No module named 'winrm'"

You probably need to install the winrm module using: sudo pip install pywinrm

Then you should see:

file4.jmpesp.xyz | SUCCESS => {
    "changed": false,
    "ping": "pong"

Execute PowerShell on hosts via Ansible


ansible windows -m win_shell -a "get-Service"

Where “windows” is the group name i specified above.

Or, create a scripts folder

sudo mkdir scripts
sudo chown scripts/

Create a simple script for testing on windows admin machine:

Set-Content script1.ps1 -Value 'Get-Service -Name wuauserv | Start-Service'

Copy it to the ansible host:

scp script1.ps1 chad@centos`:/etc/ansible/scripts

On the ansible host, use win_copy module to deploy the script:

[chad@localhost ~]$ ansible windows -m win_copy -a "src=/etc/ansible/scripts/script1.ps1 dest=c:\script.ps1"
file4.jmpesp.xyz | CHANGED => {
    "changed": true,
    "checksum": "e7117f64e52ee17063f9339c4aab9d7ef4ad77f8",
    "dest": "c:\\script.ps1",
    "operation": "file_copy",
    "original_basename": "script1.ps1",
    "size": 44,
    "src": "/etc/ansible/scripts/script1.ps1"

Run it:

[chad@localhost ~]$ ansible windows -m win_command -a "powershell.exe -ExecutionPolicy bypass -file c:\script.ps1"
file4.jmpesp.xyz | CHANGED | rc=0 >>


The real power of ansible.

We create a playbooks folder in the ansible directory on the ansible host to store the playbooks, then use SCP to copy it from our administrative machine to the ansible host.


- name: IIS
  hosts: windows
  - name: create folder
      path: C:\iis
      state: directory

  - name: create html page
      content: |
        <head><title>Ansible Test</title></head>
        <body>Some Things</body>
      dest: C:\iis\index.html

  - name: install IIS features
      name: Web-Server
      state: present
      include_sub_features: yes
      include_management_tools: no

  - name: remove default website
      name: Default Web Site
      state: absent

  - name: create new IIS website
      name: IIS Demo
      state: started
      port: 8080
      physical_path: C:\iis
  - name: Copy PowerShell scripts
      src: /etc/ansible/scripts/iissetup.ps1
      dest: 'C:\'
      remote_src: no

  - name: Execute PowerShell script
    win_command: powershell.exe -ExecutionPolicy Bypass -File C:\iissetup.ps1

We execute the playbook with:

ansible-playbook iissetup.yml -vv

On the client side:

You see processes created as children of a winrshost.exe (winrm) parent.


They’ll be sent over as encoded strings


The full command was:


The encoded part comes to:

&chcp.com 65001 > $null
$exec_wrapper_str = $input | Out-String
$split_parts = $exec_wrapper_str.Split(@("`0`0`0`0"), 2, [StringSplitOptions]::RemoveEmptyEntries)
If (-not $split_parts.Length -eq 2) { throw "invalid payload" }
Set-Variable -Name json_raw -Value $split_parts[1]
$exec_wrapper = [ScriptBlock]::Create($split_parts[0])

Add custom Powershell to the Ansible Runbook

The example runbook above demonstrated this at the bottom, but its important so here we go again. We copy the script to the target machine, then we execute the script:

  - name: Copy PowerShell scripts
      src: /etc/ansible/scripts/iissetup.ps1
      dest: 'C:\'
      remote_src: no

  - name: Execute PowerShell script
    win_command: powershell.exe -ExecutionPolicy Bypass -File C:\iissetup.ps1
Chad Duffey

Security Engineer