Related docs:
PRINCIPAL CAPABILITIES PRINCIPAL USE CASES IDM VAULT PLUGIN AAP INTEGRATION DOCS MAP
eigenstate.ipa.principal queries Kerberos principal state from FreeIPA/IdM
from Ansible.
This reference covers:
show and find operations differThe principal does not need to be a global IdM administrator. It only needs rights to query the specific object types it will inspect.
flowchart LR
task["Ansible task or assert"]
auth["Kerberos auth"]
lookup["eigenstate.ipa.principal"]
ipa["ipalib principal APIs"]
out["State record\nexists, has_keytab, disabled, last_auth"]
task --> lookup
auth --> lookup
lookup --> ipa
ipa --> out
The lookup uses the IdM Python client libraries directly through ipalib.
All queries go through the same authenticated RPC transport as the vault and
keytab plugins.
The lookup always operates with a Kerberos credential cache.
It can get there in three ways:
ipaadmin_password:
kerberos_keytab:
[!IMPORTANT] This plugin requires
python3-ipalibandpython3-ipaclienton the Ansible controller or execution environment. Install withdnf install python3-ipalib python3-ipaclient.
TLS behavior:
verify: /path/to/ca.crt enables explicit certificate verificationverify first tries /etc/ipa/ca.crtipalibWhen principal_type is left at the default auto, the plugin infers the
IdM object type from the principal name format:
| Name form | Detected type | Lookup argument |
|---|---|---|
HTTP/web.example.com |
service | full name as-is |
HTTP/web.example.com@REALM |
service | name stripped of realm |
host/web.example.com |
host | FQDN after host/ |
host/web.example.com@REALM |
host | FQDN after host/, realm stripped |
admin |
user | uid as-is |
admin@REALM |
user | uid before @ |
flowchart LR
name["Principal name"]
slash{"Contains /?"}
hostpfx{"Starts with host/?"}
svc["Service principal"]
host["Host principal"]
user["User principal"]
name --> slash
slash -->|yes| hostpfx
slash -->|no| user
hostpfx -->|yes| host
hostpfx -->|no| svc
When principal_type is set explicitly to user, host, or service,
detection is skipped and the name is handled accordingly.
find with principal_type=auto raises an error. You must specify the type
explicitly for find operations.
The lookup supports two operations:
show (default):
_termsexists=false rather than raising an errorfind:
principal_type must be specified (not auto)criteria is an optional text filter; omitting it returns all principals
of that typeEach state record contains:
| Field | Type | Notes |
|---|---|---|
name |
str | Input term as given |
canonical |
str | Full principal with @REALM; for hosts, host/fqdn@REALM when available |
type |
str | user, host, or service |
exists |
bool | false when IdM returned NotFound |
has_keytab |
bool | false when exists=false |
disabled |
bool or null | true when nsaccountlock=true (users only); null for host and service principals |
last_auth |
str or null | ISO 8601 timestamp from krblastsuccessfulauth (users only, requires IdM audit enabled); null otherwise |
record (default)Returns a list of state dictionaries, one per input principal:
principal_states: "{{ lookup('eigenstate.ipa.principal',
'HTTP/web01.example.com',
'ldap/ldap01.example.com',
server='idm-01.example.com',
kerberos_keytab='/etc/admin.keytab') }}"
# principal_states[0].name == 'HTTP/web01.example.com'
# principal_states[1].name == 'ldap/ldap01.example.com'
For a single principal, lookup(...) returns the record directly as a mapping.
query(...) returns a one-element list, so use | first there if you want the
record itself.
map_recordReturns a dictionary keyed by input principal name:
state_map: "{{ lookup('eigenstate.ipa.principal',
'HTTP/web01.example.com',
'ldap/ldap01.example.com',
server='idm-01.example.com',
kerberos_keytab='/etc/admin.keytab',
result_format='map_record') }}"
# state_map['HTTP/web01.example.com'].exists == true
# state_map['ldap/ldap01.example.com'].exists == false
The map form is useful when several principals are checked in one call and the playbook should not depend on positional list ordering.
Single service principal existence check:
- ansible.builtin.assert:
that:
- principal_state.exists
- principal_state.has_keytab
fail_msg: "Service principal missing or has no keys"
vars:
principal_state: "{{ lookup('eigenstate.ipa.principal',
'HTTP/web01.corp.example.com',
server='idm-01.corp.example.com',
ipaadmin_password=lookup('env', 'IPA_ADMIN_PASSWORD'),
verify='/etc/ipa/ca.crt') }}"
Host enrollment check before requesting a cert:
- ansible.builtin.set_fact:
host_state: "{{ lookup('eigenstate.ipa.principal',
'host/node01.corp.example.com',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/admin.keytab',
verify='/etc/ipa/ca.crt') }}"
User lock state before automation:
- ansible.builtin.set_fact:
user_state: "{{ lookup('eigenstate.ipa.principal',
'svc-deploy',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/admin.keytab',
verify='/etc/ipa/ca.crt') }}"
Multiple principals with named-map output:
- ansible.builtin.set_fact:
states: "{{ lookup('eigenstate.ipa.principal',
'HTTP/web01.corp.example.com',
'ldap/ldap01.corp.example.com',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/admin.keytab',
result_format='map_record',
verify='/etc/ipa/ca.crt') }}"
Find all service principals:
- ansible.builtin.set_fact:
all_services: "{{ lookup('eigenstate.ipa.principal',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/admin.keytab',
operation='find',
principal_type='service',
verify='/etc/ipa/ca.crt') }}"
Find service principals matching a criteria string:
- ansible.builtin.set_fact:
http_services: "{{ lookup('eigenstate.ipa.principal',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/admin.keytab',
operation='find',
principal_type='service',
criteria='HTTP',
verify='/etc/ipa/ca.crt') }}"
Common failure classes:
ipalib or ipaclient libraries on the controller or EEfind called with principal_type=auto — type must be specifiedshow called without any principal names in _termsAuthorizationError from ipalib — principal lacks rights to query the
object type[!NOTE] A principal that does not exist in IdM returns a state record with
exists=falserather than raising an error. Use this for conditional pre-flight logic withoutignore_errors.
Use PRINCIPAL CAPABILITIES when you need operator patterns rather than option-by-option reference: