Nearby docs:
IDM VAULT PLUGIN IDM VAULT CAPABILITIES INVENTORY USE CASES DOCS MAP
This page contains worked examples for eigenstate.ipa.vault against IdM
vaults.
Use the capability guide to choose vault ownership and retrieval patterns. Use this page when you need the corresponding playbook or controller pattern.
Not every caller is a global IdM administrator. A delegated operator can use IdM vault lookups inside their own jurisdiction if the vault ownership and IdM permissions line up with the scope they are responsible for.
flowchart LR
need["Runtime secret need"]
scope["Choose vault scope"]
form["Choose text or binary return form"]
task["Inject into task,\nfile, or credential flow"]
need --> scope --> form --> task
Use a shared standard vault when many jobs need the same text secret and no extra decrypt material should be involved.
This is a common non-admin pattern when a team owns a shared service and the vault is scoped to that service or team rather than to the entire IdM domain.
flowchart LR
V["shared database-password vault"] --> L["lookup(..., shared=true)"] --> T["template or app config"]
Example:
- name: Retrieve database password
ansible.builtin.set_fact:
db_password: "{{ lookup('eigenstate.ipa.vault',
'database-password',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
verify='/etc/ipa/ca.crt') }}"
- name: Render application config
ansible.builtin.template:
src: app.conf.j2
dest: /etc/myapp/app.conf
Use a symmetric vault when the secret is centrally owned but protected by an additional vault password.
This is also a strong delegated-operator pattern when the operator can read the vault through a team-scoped principal but the shared password is distributed through a separate secret channel.
flowchart LR
V["shared symmetric vault"] --> P["vault_password_file"] --> L["lookup"] --> T["runtime API auth"]
Example:
- name: Retrieve partner API key
ansible.builtin.set_fact:
partner_api_key: "{{ lookup('eigenstate.ipa.vault',
'partner-api-key',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
vault_password_file='/runner/env/ipa/partner-api.pass',
verify='/etc/ipa/ca.crt') }}"
Use an asymmetric vault when retrieval should require a local private key in addition to IdM-side authorization.
That is useful for a narrow platform team that is allowed to recover only its own TLS material.
flowchart LR
V["shared asymmetric vault"] --> K["private_key_file"] --> L["lookup"] --> F["materialize private key"]
Example:
- name: Recover wildcard TLS key
ansible.builtin.copy:
content: "{{ lookup('eigenstate.ipa.vault',
'wildcard-tls-key',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
private_key_file='/runner/env/ipa/tls-recovery.pem',
verify='/etc/ipa/ca.crt') }}"
dest: /etc/pki/tls/private/wildcard.key
mode: "0600"
When the vault payload is binary, return base64 and decode at the edge.
This is one of the strongest Kerberos-adjacent use cases because the retrieved artifact is itself a Kerberos credential material.
flowchart LR
V["service-keytab vault"] --> E["encoding=base64"] --> L["lookup"] --> F["b64decode to /etc/krb5.keytab"]
Example:
- name: Install service keytab from IdM vault
ansible.builtin.copy:
content: "{{ lookup('eigenstate.ipa.vault',
'service-keytab',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
encoding='base64',
verify='/etc/ipa/ca.crt') | b64decode }}"
dest: /etc/krb5.keytab
mode: "0600"
Use a user vault when the secret belongs to one user principal rather than to a shared automation namespace.
This fits a delegated user or application owner who should be able to retrieve their own bootstrap material without full IdM admin rights.
flowchart LR
V["user vault"] --> S["username='appuser'"] --> L["lookup"] --> T["principal-scoped bootstrap"]
Example:
- name: Retrieve bootstrap token for appuser
ansible.builtin.debug:
msg: "{{ lookup('eigenstate.ipa.vault',
'bootstrap-token',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
username='appuser',
verify='/etc/ipa/ca.crt') }}"
Use a service vault when the secret boundary should mirror the service identity.
This is the most natural non-admin service-account pattern in the collection.
flowchart LR
V["service vault"] --> S["service='HTTP/app.corp.example.com'"] --> L["lookup"] --> T["service-scoped config"]
Example:
- name: Retrieve OIDC client secret
ansible.builtin.set_fact:
oidc_client_secret: "{{ lookup('eigenstate.ipa.vault',
'oidc-client-secret',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
service='HTTP/app.corp.example.com',
verify='/etc/ipa/ca.crt') }}"
One of the better controller uses is to resolve secrets from IdM at runtime instead of storing them statically in controller fields.
That pattern is especially useful for delegated operators because the controller can hold a team keytab rather than a reusable admin password.
flowchart LR
C["AAP job"] --> L["IdM vault lookup in task or injector"]
L --> I["IdM vault"]
I --> R["latest rotated value at runtime"]
Example:
- name: Resolve runtime secret in controller
ansible.builtin.set_fact:
runtime_secret: "{{ lookup('eigenstate.ipa.vault',
'db-password',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
verify='/runner/env/ipa/ca.crt') }}"
The operational payoff of the vault plugin is clean reruns after rotation.
flowchart LR
R["Rotate secret in IdM"] --> J["Rerun job"] --> L["lookup resolves new value"] --> O["no Git change required"]
Use this when:
Kerberos is profitable here because the same non-admin service principal can be used before and after rotation, so the operator rotates the vault content rather than the auth mechanism.
Use an IdM OTP when the job is not trying to manage a long-lived secret at all, but instead needs a one-time bootstrap credential that leads into a normal Kerberos-backed automation flow.
flowchart LR
otp["OTP or enrollment password"]
enroll["Host enrollment\nor first login"]
kt["Kerberos ticket or keytab"]
lookup["Vault lookup"]
secret["Runtime secret"]
otp --> enroll --> kt --> lookup --> secret
This is the right mental model when the real goal is:
Example:
- name: Install a host using an OTP-based enrollment credential
ansible.builtin.command:
cmd: >-
ipa-client-install
--principal=admin
--password="{{ otp_enrollment_password }}"
--unattended
- name: Retrieve the post-enrollment service secret from IdM vault
ansible.builtin.set_fact:
app_service_password: "{{ lookup('eigenstate.ipa.vault',
'app-service-password',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/admin.keytab',
shared=true,
verify='/etc/ipa/ca.crt') }}"
Why this matters:
app-service-password[!NOTE] OTP is useful here because it can be consumed once and then disappear from the bootstrap path. That makes it a good fit for enrollment and first-login workflows, but not a replacement for leased or dynamically issued secrets.
Use operation='show' or operation='find' when the automation needs to
inspect vault metadata before trying to consume the payload.
flowchart LR
lookup["eigenstate.ipa.vault"] --> show["operation=show\ninspect one known vault"]
lookup --> find["operation=find\nsearch selected scope"]
show --> decision["decide whether the payload should be retrieved"]
find --> decision
This is useful when:
Example:
- name: Inspect one known vault
ansible.builtin.set_fact:
vault_info: "{{ lookup('eigenstate.ipa.vault',
'database-password',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
operation='show',
result_format='record') }}"
- name: Find matching shared vaults
ansible.builtin.set_fact:
matching_vaults: "{{ lookup('eigenstate.ipa.vault',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
operation='find',
criteria='database',
result_format='map_record') }}"
Use a stable metadata convention when the vault is holding a sealed or encrypted artifact that Ansible will broker to another system.
Recommended fields:
payments-bootstrap-bundle or cluster-sealed-payloadstandard unless the vault itself needs additional unlock materialbase64, utf-8, or another downstream
application formatExample metadata:
sealed_artifact:
vault_name: payments-bootstrap-bundle
description: sealed bootstrap bundle for payments-agent
consumer: payments-agent
final_boundary: target-host
format: base64
Use this pattern when IdM should authorize and store an opaque encrypted artifact, Ansible should deliver it, and a downstream system should do the final consume or unseal step.
flowchart LR
vault["Shared vault with sealed payload"]
lookup["Lookup returns opaque blob"]
broker["AAP or Ansible job"]
target["Target host\nor downstream controller"]
consume["Local consume or unseal"]
vault --> lookup --> broker --> target --> consume
This is useful when:
Detailed example:
- name: Retrieve sealed bootstrap bundle and its vault metadata
ansible.builtin.set_fact:
bootstrap_bundle: "{{ lookup('eigenstate.ipa.vault',
'payments-bootstrap-bundle',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/payments-svc.keytab',
shared=true,
encoding='base64',
result_format='record',
include_metadata=true,
verify='/etc/ipa/ca.crt') }}"
- name: Materialize the opaque bundle on the target host
ansible.builtin.copy:
content: "{{ bootstrap_bundle.value | b64decode }}"
dest: /var/lib/payments/bootstrap.bundle
mode: "0600"
- name: Route handling from vault metadata
ansible.builtin.debug:
msg:
- "vault={{ bootstrap_bundle.name }}"
- "type={{ bootstrap_bundle.type }}"
- "description={{ bootstrap_bundle.description }}"
- name: Hand the bundle to the local bootstrap agent
ansible.builtin.command:
argv:
- /usr/local/libexec/payments-bootstrap
- --bundle
- /var/lib/payments/bootstrap.bundle
when: bootstrap_bundle.description is search('bootstrap')
What this does well:
What it does not do:
Role-shaped example pattern:
- name: Read brokered artifact metadata
ansible.builtin.set_fact:
brokered_artifact: "{{ lookup('eigenstate.ipa.vault',
sealed_artifact.vault_name,
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/payments-svc.keytab',
shared=true,
encoding=sealed_artifact.format,
result_format='record',
include_metadata=true,
verify='/etc/ipa/ca.crt') }}"
- name: Materialize brokered artifact
ansible.builtin.copy:
content: "{{ brokered_artifact.value | b64decode }}"
dest: /var/lib/payments/bootstrap.bundle
mode: "0600"
- name: Hand off to local consumer
ansible.builtin.command:
argv:
- /usr/local/libexec/payments-bootstrap
- --bundle
- /var/lib/payments/bootstrap.bundle
when: brokered_artifact.description is search('bootstrap')
Use the output helpers when the payload is text but the caller should receive a cleaner value than the raw stored bytes.
flowchart LR
vault["UTF-8 vault payload"] --> helper["decode_json or strip_trailing_newline"]
helper --> task["consume structured config or trimmed secret"]
This is useful when:
Example:
- name: Retrieve structured JSON config
ansible.builtin.set_fact:
app_config: "{{ lookup('eigenstate.ipa.vault',
'app-config',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
decode_json=true,
verify='/etc/ipa/ca.crt') }}"
- name: Retrieve a newline-normalized password
ansible.builtin.set_fact:
db_password: "{{ lookup('eigenstate.ipa.vault',
'database-password',
server='idm-01.corp.example.com',
kerberos_keytab='/runner/env/ipa/team-svc.keytab',
shared=true,
strip_trailing_newline=true,
verify='/etc/ipa/ca.crt') }}"
The sealed artifact pattern in sections 11 and 12 has one external dependency: a CMS-usable X.509 certificate and matching private key must already exist on the target before anything can be sealed for it. Traditionally certmonger provides that pair on enrolled IdM hosts.
eigenstate.ipa.cert removes that dependency. The cert plugin can provision
the recipient certificate as part of the same Ansible play that seals and
archives the artifact — no certmonger installation or prior enrollment
state required on the target.
flowchart LR
sign["Cert plugin signs CSR"]
seal["Host cert used as recipient"]
archive["Archive sealed blob"]
deliver["Vault plugin delivers blob"]
unseal["Target unseals locally"]
sign --> seal --> archive --> deliver --> unseal
This enables two cross-plugin patterns:
Certmonger-free sealed provisioning: the controller generates the key and CSR, the cert plugin signs the cert against IdM CA, the private key is archived to a vault, the sealed artifact is archived to a second vault, and both are retrieved and delivered to the target by the vault plugin in a single play.
Coordinated cert and key provisioning: a new service that has no cert or private key yet can have both generated and vaulted in one play, with the cert plugin handling issuance and the vault plugin handling key storage and delivery.
The key handling difference between this and the traditional certmonger sealed artifact path is intentional and worth understanding before choosing. On the certmonger path, the private key is generated on the target and never moves — the sealed artifact pattern is architecturally strongest there. On the cert-plugin path, the private key passes through controller memory at generation time before being vaulted. That tradeoff is acceptable for new-host provisioning scenarios where the target does not yet have an established key pair.
Full playbooks for both patterns are in:
CERT USE CASES — 10. Certmonger-Free Sealed Artifact Provisioning
CERT USE CASES — 11. Coordinated Cert And Private Key Provisioning
Kerberos is especially useful for delegated vault work because:
Use password auth when you are bootstrapping an environment, but prefer Kerberos for steady-state secret retrieval.
For the decision model behind these scenarios, return to IDM VAULT CAPABILITIES.