eigenstate-ipa

IdM Vault Use Cases

Nearby docs:

  IDM VAULT PLUGIN     IDM VAULT CAPABILITIES     INVENTORY USE CASES     DOCS MAP  

Purpose

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.

Contents

Use Case Flow

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

1. Shared Database Password Injection

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

2. Shared API Key From A Symmetric Vault

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') }}"

3. TLS Private Key Recovery From An Asymmetric Vault

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"

4. Service Keytab Distribution

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"

5. User-Owned Bootstrap Token

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') }}"

6. Service-Principal Secret Injection

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') }}"

7. Controller Credential-Source Pattern

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') }}"

8. Rotation Without Repository Churn

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.

9. OTP Bootstrap Into Kerberos And Vault Retrieval

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:

[!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.

10. Inspect Vault Metadata Before Retrieval

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') }}"

11. Brokered Sealed Artifact Metadata Convention

Use a stable metadata convention when the vault is holding a sealed or encrypted artifact that Ansible will broker to another system.

Recommended fields:

Example metadata:

sealed_artifact:
  vault_name: payments-bootstrap-bundle
  description: sealed bootstrap bundle for payments-agent
  consumer: payments-agent
  final_boundary: target-host
  format: base64

12. Brokered Sealed Artifact Delivery

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')

13. Structured JSON And Normalized Text Retrieval

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') }}"

14. Sealed Artifact Delivery Without Certmonger

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:

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 A Good Default Here

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.