Skip to content
150 changes: 150 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,156 @@ jobs:
```

Because the workflow runs on a self-hosted runner inside the internal network, it can resolve `db-primary.internal.example.com` -- hosts that would not be reachable from GitHub-hosted runners. The action's Docker image supplies Ansible and all dependencies, so no additional setup is needed on the runner host.


## Enabling WinRM on Windows Nodes for Ansible

WinRM (Windows Remote Management) is Microsoft's implementation of the WS-Management protocol. Ansible uses WinRM to communicate with Windows hosts because Windows does not ship with an SSH server by default. Before you can manage a Windows node with Ansible, WinRM must be enabled and configured on that node.

### 1. Enable WinRM on the Windows node

Run the following commands in an **elevated PowerShell** session (Run as Administrator) on each Windows target:

```powershell
# Quick configuration of WinRM listener
winrm quickconfig -q

# Enable WSMan CredSSP (required for credential delegation)
Enable-WSManCredSSP -Role Server -Force
```

`winrm quickconfig` does the following:
- Starts the WinRM service and sets it to auto-start.
- Creates an HTTP listener on port `5985`.
- Adds a firewall exception for WS-Management traffic.

### 2. Configure the firewall

If the firewall rules are not created automatically, or if you need HTTPS access on port `5986`, add them manually:

```powershell
New-NetFirewallRule -DisplayName "Allow WinRM TCP 5985 (HTTP)" -Direction Inbound -Protocol TCP -LocalPort 5985 -Action Allow
New-NetFirewallRule -DisplayName "Allow WinRM TCP 5986 (HTTPS)" -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow
```

| Port | Protocol | Use |
|------|----------|-----|
| 5985 | HTTP | Default WinRM listener (can be encrypted via Kerberos/NTLM) |
| 5986 | HTTPS | WinRM over SSL (requires a certificate) |

### 3. Set up HTTPS with a self-signed certificate (optional but recommended)

For production environments, use HTTPS with a valid certificate signed by a trusted CA. For testing, you can create a self-signed certificate:

```powershell
# Create a self-signed certificate
$cert = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My `
-DnsName "$env:COMPUTERNAME" -FriendlyName "WinRM HTTPS"

# Create the HTTPS listener
New-Item -Path WSMan:\Localhost\Listener -Transport HTTPS -Address * `
-CertificateThumbprint $cert.Thumbprint -Force
```

When using a self-signed certificate, set `ansible_winrm_server_cert_validation=ignore` in your Ansible inventory to bypass certificate validation.

### 4. Verify WinRM is listening

On the Windows node itself, confirm the listeners are active:

```powershell
winrm enumerate winrm/config/Listener
```

You should see output similar to:

```
Listener
Address = *
Transport = HTTP
Port = 5985
Enabled = true
...

Listener
Address = *
Transport = HTTPS
Port = 5986
Enabled = true
...
```

### 5. Test WinRM connectivity from the control node

From the Ansible control node (or the GitHub Actions runner), verify you can reach the Windows host over WinRM **before** running playbooks:

```bash
# Test HTTP (port 5985)
ansible windows -i inventory.ini -m ansible.windows.win_ping

# Or use the ansible-cli action locally via Docker
docker run --rm eftechcombr/ansible-cli-github-action \
ansible windows -i inventory.ini -m ansible.windows.win_ping
```

If connectivity fails, check that:
- The Windows node is reachable from the runner (no firewall blocking).
- The WinRM service is running (`Get-Service WinRM`).
- The `ansible_user` has the necessary permissions.
- CredSSP or the chosen transport (NTLM, Kerberos) is correctly configured.

### 6. Inventory variables for Windows nodes

When using the `ansible-cli` action with Windows targets, your inventory must include the following variables:

```ini
[windows]
windows-server-01.example.com

[windows:vars]
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_user=Administrator
ansible_password={{ windows_admin_password }}
ansible_winrm_server_cert_validation=ignore
```

| Variable | Value | Purpose |
|----------|-------|---------|
| `ansible_connection` | `winrm` | Tells Ansible to use the WinRM connection plugin instead of SSH. |
| `ansible_winrm_transport` | `ntlm` / `credssp` / `kerberos` | Authentication protocol. NTLM works for most environments; CredSSP is needed for credential delegation (e.g., when the playbook needs to pass credentials to a second host). |
| `ansible_user` | `Administrator` | Local or domain user with admin privileges. |
| `ansible_password` | `{{ password_variable }}` | The user's password (use secrets in CI). |
| `ansible_winrm_server_cert_validation` | `ignore` | Required when using a self-signed certificate. Omit or set to `validate` for CA-signed certs. |

### 7. Common issues and tips

**Python fork-safety on macOS**
If you are developing or testing locally on macOS with zsh, you may encounter crashes when Ansible spawns subprocesses. Add this to your `.zshrc`:

```bash
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
```

**pywinrm is pre-installed**
The `ansible-cli-github-action` Docker image includes `pywinrm[credssp]`, so no additional Python packages are needed when running inside GitHub Actions. For local testing, install it with:

```bash
python3 -m venv env
source env/bin/activate
pip install pywinrm[credssp] ansible
```

**Transport selection**
- **NTLM** — Simplest; works across domains; does not support double-hop (passing credentials to a second machine).
- **CredSSP** — Enables double-hop; requires `Enable-WSManCredSSP -Role Server -Force` on the Windows host.
- **Kerberos** — Best for domain-joined hosts; provides the strongest security but requires additional setup (keytab or `kinit`).

**Reference links**
- [Ansible Windows WinRM guide](https://docs.ansible.com/ansible/latest/os_guide/windows_winrm.html)
- [Homebrew and Python PEP 668](https://docs.brew.sh/Homebrew-and-Python#pep-668-python312-and-virtual-environments)


## Testing

Run the smoke test suite locally to verify the Docker image builds and all Ansible tools work correctly:
Expand Down
Loading