FreeIPA CLI on Windows 11
- There is no native
ipa.exe.ipa-clientand theipacommand-line are Linux-only. - Recommended path: WSL2 + a tiny
ipa.batlauncher. It feels like a real Windows command and does not require giving up WSL features. - Fallback path: SSH from Windows into the IPA server and run
ipathere. - Advanced path: call the FreeIPA JSON-RPC API from native Python on Windows with
ipalib. Only worth it for building tooling. - All three paths need the prereqs in Windows 11 + FreeIPA (CA trust, DNS, time, Kerberos config) done first.
Why there is no ipa.exe
The ipa command is a Python tool built on ipalib. ipalib itself can technically import on Windows, but it depends on Kerberos libraries and a working enrollment that only ships as part of ipa-client. ipa-client uses SSSD, systemd, and a pile of RPM-delivered policy files that have no Windows equivalent. Red Hat has never shipped a supported Windows binary. So the choice is: use Linux, proxy via SSH, or talk to the API directly.
Path A (recommended): WSL2 + ipa.bat
Install WSL2 with a distro that has freeipa-admintools
Fedora is the easiest because FreeIPA is a Red Hat project; Rocky Linux or AlmaLinux work identically and are stable. In an elevated PowerShell:
wsl --install --no-distribution
wsl --set-default-version 2
wsl --install -d FedoraLinux-42 # or: Rocky Linux, AlmaLinux
If a Fedora/Rocky WSL distro is not available in the Microsoft Store for your build, you can import a rootfs tarball:
wsl --import Fedora C:\wsl\Fedora C:\Downloads\fedora-rootfs.tar.xz --version 2
wsl -d Fedora
Install the IPA admin tools inside WSL
sudo dnf install -y freeipa-admintools krb5-workstation ca-certificates
You do not need to run ipa-client-install in WSL for the CLI to work — freeipa-admintools pulls in ipa alone. Only run ipa-client-install if you specifically want WSL enrolled as an IPA host.
Wire up CA trust and Kerberos inside WSL
# Fedora/Rocky/Alma
sudo cp /mnt/c/certs/ipa-ca.crt /etc/pki/ca-trust/source/anchors/ipa-ca.crt
sudo update-ca-trust
# /etc/krb5.conf — minimal
sudo tee /etc/krb5.conf >/dev/null <<'EOF'
[libdefaults]
default_realm = EXAMPLE.INTERNAL
dns_lookup_realm = true
dns_lookup_kdc = true
rdns = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
[realms]
EXAMPLE.INTERNAL = {
kdc = ipa.example.internal
master_kdc = ipa.example.internal
admin_server = ipa.example.internal
default_domain = example.internal
}
[domain_realm]
.example.internal = EXAMPLE.INTERNAL
example.internal = EXAMPLE.INTERNAL
EOF
If your WSL does not inherit Windows DNS, add the IPA DNS to /etc/resolv.conf (and use /etc/wsl.conf with [network] generateResolvConf=false to stop WSL overwriting it).
First run — get a ticket, list users
kinit admin@EXAMPLE.INTERNAL
klist
ipa user-find
ipa host-show $(hostname -f) || true
If ipa user-find returns rows, WSL-side is ready. Now we make it feel like a native Windows command.
Make ipa a Windows command — ipa.bat
Create C:\Tools\ipa.bat (or anywhere on your PATH):
@echo off
setlocal
rem Drop into WSL, renew a TGT if we still have one, then run `ipa` with all args.
rem If there is no TGT yet, `kinit -R` will fail silently and ipa will prompt.
wsl.exe -d Fedora -- bash -lc "kinit -R 2>/dev/null; ipa %*"
endlocal
Make sure C:\Tools is on PATH (System Properties → Environment Variables → User Path → Edit → New → C:\Tools). Open a new terminal and:
ipa user-find
ipa host-show $(hostname -f)
ipa --version
That is it — ipa is now callable from cmd.exe, PowerShell, Windows Terminal, anything.
A one-command ticket refresh (kinit.bat)
Pair it with C:\Tools\kinit.bat so you do not have to wsl in manually when your ticket expires:
@echo off
setlocal
if "%1"=="" (
wsl.exe -d Fedora -- bash -lc "kinit"
) else (
wsl.exe -d Fedora -- bash -lc "kinit %*"
)
wsl.exe -d Fedora -- bash -lc "klist"
endlocal
kinit admin # prompts for password, stores ticket in the WSL ccache
kinit -r 7d admin # renewable 7-day ticket
Path B: SSH to the IPA server
No WSL allowed? Jump box style works. Windows 11 ships ssh.exe (OpenSSH). Install the IPA CA (see Windows 11 + FreeIPA), then:
ssh admin@ipa.example.internal
# inside the IPA server:
kinit
ipa user-find
Two quality-of-life improvements:
A Windows Terminal profile named "IPA"
Settings → Profiles → New. Command line:
ssh -t admin@ipa.example.internal "tmux new -A -s ipa || bash -l"
That pins your "IPA shell" to a tab in Windows Terminal — click it and you are in.
Kerberos SSO to the IPA SSH server
If you have a working Windows SSPI Kerberos (see next section), add GSSAPIAuthentication=yes to your SSH command or C:\Users\you\.ssh\config:
Host ipa ipa.example.internal
HostName ipa.example.internal
User admin
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
Then ssh ipa uses your existing ticket — no password.
When to pick this path: Windows hosts are corp-locked down, WSL is blocked by policy, but ssh to internal infra is permitted. It is also the right path for anyone who already lives in an IPA bastion workflow.
Path C: native Python + REST (ipalib/JSON-RPC)
FreeIPA exposes a JSON-RPC API at https://<ipa-server>/ipa/json. With the IPA CA trusted (the Windows step from the foundation page) and Kerberos set up on Windows, you can drive it from native Python.
py -m venv C:\Tools\ipa-py
C:\Tools\ipa-py\Scripts\Activate.ps1
pip install ipalib requests-kerberos
# ipa_show_user.py
import sys
from ipalib import api
api.bootstrap(context='cli', in_server=False, server='ipa.example.internal')
api.finalize()
api.Backend.rpcclient.connect()
result = api.Command.user_show(sys.argv[1] if len(sys.argv) > 1 else 'admin')
print(result['result']['uid'], result['result'].get('mail'))
python .\ipa_show_user.py admin
ipalib delegates auth to the system Kerberos stack via gssapi. If that is not importable on Windows (it frequently is not), fall back to REST:
import json, requests
from requests_kerberos import HTTPKerberosAuth, OPTIONAL
s = requests.Session()
s.verify = r'C:\certs\ipa-ca.crt'
s.headers.update({'referer': 'https://ipa.example.internal/ipa'})
auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL)
# Login once (writes cookie)
s.get('https://ipa.example.internal/ipa/session/login_kerberos', auth=auth)
# Call user_show
body = {'method': 'user_show', 'params': [['admin'], {}], 'id': 0}
r = s.post('https://ipa.example.internal/ipa/session/json', json=body)
print(json.dumps(r.json()['result']['result'], indent=2, default=str))
When to pick this path: building native Windows tooling, packaging into an .exe, or anywhere the lack of WSL and lack of SSH is a hard constraint but Python is fine.
Kerberos on Windows for Paths B and C
Both rely on a working Windows-side Kerberos. You have two choices:
MIT Kerberos for Windows (simplest)
- Install from MIT. Provides
kinit.exe,klist.exe,kdestroy.exe, a systray app, and the Network Identity Manager. - Reads the same
C:\ProgramData\MIT\Kerberos5\krb5.inidescribed in the foundation page. - Tickets go into the MIT ccache, separate from the Windows LSA cache.
Native Windows SSPI (via ksetup)
- Configured with
ksetup /AddKdc(see foundation page). - Works for Edge/Chrome SSO,
ssh -K, and anything that callsInitializeSecurityContext. - Tickets managed by LSA; list with
klist(the Windows built-in, not MIT), purge withklist purge.
ssh.exe and most Python GSSAPI bindings use SSPI on Windows 11; MIT KfW is the simplest way to get kinit admin to "just work" outside of WSL.
Making it feel like a real command
Start Menu shortcut for Path A
Right-click Desktop → New → Shortcut. Target: C:\Tools\ipa.bat. Pin to Start. Now "ipa" is right next to Edge.
Windows Terminal profile
Add a profile with commandLine = wsl.exe -d Fedora -- bash -lc "kinit -R 2>/dev/null; exec bash -l". The tab opens pre-authenticated (if the ticket is still live) into Fedora. Run ipa there for interactive sessions.
PowerShell functions
Add to your $PROFILE:
function ipa { wsl.exe -d Fedora -- bash -lc "kinit -R 2>/dev/null; ipa $($args -join ' ')" }
function kinit-ipa { wsl.exe -d Fedora -- bash -lc "kinit $($args -join ' '); klist" }
Now tab-complete in PowerShell: ipa user-find, ipa host-show ..., kinit-ipa admin.
Common gotchas
| Symptom | Cause | Fix |
|---|---|---|
kinit: Clock skew too great | Windows clock off by >5 min from IPA | Re-sync with w32tm /resync, point at the IPA NTP (see foundation page) |
kinit: Cannot contact any KDC for realm | Windows cannot resolve IPA SRV records | Point DNS at IPA, or keep explicit kdc = lines in krb5.ini |
kinit: KDC reply did not match expectations | Realm name case mismatch | Realm is case-sensitive — EXAMPLE.INTERNAL, not example.internal |
Server not found in Kerberos database | Missing reverse DNS for the IPA server | Add a PTR in IPA DNS, or set rdns = false in krb5.ini |
TLS error connecting to ipa.example.internal | IPA CA not in whichever trust store the tool uses | Import the CA per foundation page; for WSL, update-ca-trust |
ipa: command not found in WSL | freeipa-admintools not installed | sudo dnf install freeipa-admintools |
ipa.bat runs but %* expands weirdly | cmd.exe quoting of commas, equals, parens | Quote the whole arg: ipa "user-mod name --first=Foo" or use the PowerShell function version above |
| Ticket dies every 10 hours and is not renewable | Initial kinit not renewable | kinit -r 7d username |
Python ipalib import fails on Windows | Missing python-gssapi / MIT libs | Fall back to the REST pattern in Path C |
When to pick which
| Constraint | Best path |
|---|---|
| No constraints, daily-driver admin | Path A (WSL2 + ipa.bat) |
| WSL blocked by corporate policy | Path B (SSH) |
| SSH to IPA blocked by segmentation | Path A (WSL2) talks LDAP/HTTPS, not SSH |
| Need to package a Windows GUI/CLI tool | Path C (Python + REST) |
| Heavy batch admin / scripts | Path A — fastest, full ipa surface, scripts the same as on the IPA servers |
| Just one-off "show me this user" | Path B — nothing to install |
ipa CLI running is to set up or troubleshoot Keycloak on Windows + LDAPS, continue there.