eigenstate-ipa

IdM Vault Write Use Cases

Related docs:

  VAULT WRITE PLUGIN     VAULT WRITE CAPABILITIES     IDM VAULT USE CASES     ROTATION USE CASES     DOCS MAP  

Purpose

This page contains worked examples for eigenstate.ipa.vault_write.

Use the capability guide to choose the right vault lifecycle pattern. Use this page when you need the corresponding playbook.

Contents


1. Create a Standard Vault

Create a shared standard vault that will hold an automation secret. The vault is empty after creation. A subsequent archive task populates it.

- name: Ensure rotation-target vault exists
  eigenstate.ipa.vault_write:
    name: rotation-target
    state: present
    shared: true
    description: "Database credential targeted for rotation automation"
    server: idm-01.example.com
    kerberos_keytab: /etc/ipa/automation.keytab
    ipaadmin_principal: automation@EXAMPLE.COM
    verify: /etc/ipa/ca.crt
  register: vault_create

- ansible.builtin.debug:
    msg: "Vault created: {{ vault_create.changed }}"

Why this pattern:


2. Archive a Static Secret

Store a known static credential into an existing vault. Useful for bootstrapping a vault with its initial value.

- name: Archive initial database password
  eigenstate.ipa.vault_write:
    name: db-password
    state: archived
    shared: true
    data: "{{ initial_db_password }}"
    server: idm-01.example.com
    ipaadmin_password: "{{ lookup('env', 'IPA_ADMIN_PASSWORD') }}"
    verify: /etc/ipa/ca.crt
  no_log: true

For a file-based payload:

- name: Archive configuration blob from file
  eigenstate.ipa.vault_write:
    name: app-config-blob
    state: archived
    shared: true
    data_file: /runner/env/secrets/app-config.json
    server: idm-01.example.com
    kerberos_keytab: /etc/ipa/automation.keytab
    ipaadmin_principal: automation@EXAMPLE.COM

Why this pattern:


3. Rotate a Secret In Place

Retrieve the current credential, generate a replacement, archive the new value, and notify consumers. This is the primary operational use case for the vault write module.

- name: Rotate app-secret
  hosts: localhost
  gather_facts: false

  vars:
    ipa_server: idm-01.example.com
    ipa_keytab: /etc/ipa/automation.keytab
    ipa_principal: automation@EXAMPLE.COM

  tasks:
    - name: Retrieve current secret
      ansible.builtin.set_fact:
        current_secret: >-
          {{ lookup('eigenstate.ipa.vault', 'app-secret',
                    server=ipa_server,
                    kerberos_keytab=ipa_keytab,
                    ipaadmin_principal=ipa_principal,
                    shared=true,
                    verify='/etc/ipa/ca.crt') }}
      no_log: true

    - name: Generate new 32-character secret
      ansible.builtin.set_fact:
        new_secret: "{{ lookup('community.general.random_string',
                               length=32, special=false) }}"
      no_log: true

    - name: Archive rotated secret
      eigenstate.ipa.vault_write:
        name: app-secret
        state: archived
        shared: true
        data: "{{ new_secret }}"
        server: "{{ ipa_server }}"
        kerberos_keytab: "{{ ipa_keytab }}"
        ipaadmin_principal: "{{ ipa_principal }}"
        verify: /etc/ipa/ca.crt
      no_log: true
      register: rotation_result

    - name: Report outcome
      ansible.builtin.debug:
        msg: >-
          Rotation {{ 'completed' if rotation_result.changed else 'skipped (no change)' }}

Why this pattern:


4. Delete a Vault

Remove a vault when it is no longer needed. The state: absent step is idempotent: re-running the play when the vault is already gone makes no change.

- name: Decommission service credential vault
  eigenstate.ipa.vault_write:
    name: old-service-credential
    state: absent
    shared: true
    server: idm-01.example.com
    ipaadmin_password: "{{ lookup('env', 'IPA_ADMIN_PASSWORD') }}"
    verify: /etc/ipa/ca.crt
  register: vault_delete

- ansible.builtin.debug:
    msg: >-
      {{ 'Vault deleted' if vault_delete.changed else 'Vault was already absent' }}

[!CAUTION] Vault deletion is not recoverable. If the vault contains active secrets, retrieve and store them in another location before running state: absent. Confirm the vault name and scope before executing a deletion play in production.


5. Add a Service Account as a Vault Member

Grant a service principal read access to an existing vault by adding it as a vault member. The module uses delta-only reconciliation: it only makes the API call if the principal is not already a member.

- name: Grant automation service account access to app-secret vault
  eigenstate.ipa.vault_write:
    name: app-secret
    state: present
    shared: true
    members:
      - HTTP/automation.example.com@EXAMPLE.COM
    server: idm-01.example.com
    ipaadmin_password: "{{ lookup('env', 'IPA_ADMIN_PASSWORD') }}"
    verify: /etc/ipa/ca.crt

- name: Remove a decommissioned service account from vault membership
  eigenstate.ipa.vault_write:
    name: app-secret
    state: present
    shared: true
    members_absent:
      - HTTP/old-automation.example.com@EXAMPLE.COM
    server: idm-01.example.com
    ipaadmin_password: "{{ lookup('env', 'IPA_ADMIN_PASSWORD') }}"
    verify: /etc/ipa/ca.crt

Why this pattern:


6. Bootstrap Credential for a New Host

When a new host is enrolled into IdM, provision its bootstrap credential in a single play: create the vault, archive the initial credential, and grant access to the host’s service principal.

- name: Bootstrap host credential
  hosts: localhost
  gather_facts: false

  vars:
    host: appserv-01.example.com
    ipa_server: idm-01.example.com
    ipa_keytab: /etc/ipa/automation.keytab
    ipa_principal: automation@EXAMPLE.COM

  tasks:
    - name: Generate bootstrap token
      ansible.builtin.set_fact:
        bootstrap_token: "{{ lookup('community.general.random_string',
                                    length=24, special=false) }}"
      no_log: true

    - name: Create and populate bootstrap vault
      eigenstate.ipa.vault_write:
        name: "bootstrap-{{ host }}"
        state: archived
        shared: true
        data: "{{ bootstrap_token }}"
        description: "Bootstrap credential for {{ host }}"
        members:
          - "host/{{ host }}@EXAMPLE.COM"
        server: "{{ ipa_server }}"
        kerberos_keytab: "{{ ipa_keytab }}"
        ipaadmin_principal: "{{ ipa_principal }}"
        verify: /etc/ipa/ca.crt
      no_log: true
      register: bootstrap_result

    - name: Confirm vault ready
      ansible.builtin.assert:
        that:
          - bootstrap_result.vault.name == "bootstrap-" ~ host
          - bootstrap_result.vault.scope == "shared"
        fail_msg: "Bootstrap vault not in expected state"

Why this pattern:


7. Archive a Private Key After Certificate Issuance

After requesting a certificate via eigenstate.ipa.cert, the private key is temporarily on the controller. Archive it into IdM immediately so the controller does not hold it as a long-term secret.

- name: Request certificate
  eigenstate.ipa.cert:
    operation: request
    principal: "HTTP/app01.example.com"
    server: idm-01.example.com
    kerberos_keytab: /etc/ipa/automation.keytab
    ipaadmin_principal: automation@EXAMPLE.COM
    verify: /etc/ipa/ca.crt
  register: cert_result

- name: Ensure private key vault exists
  eigenstate.ipa.vault_write:
    name: "tls-key-app01"
    state: present
    shared: true
    description: "TLS private key for app01  archived after issuance"
    server: idm-01.example.com
    kerberos_keytab: /etc/ipa/automation.keytab
    ipaadmin_principal: automation@EXAMPLE.COM
    verify: /etc/ipa/ca.crt

- name: Archive private key into vault
  eigenstate.ipa.vault_write:
    name: "tls-key-app01"
    state: archived
    shared: true
    data_file: "/tmp/app01-private.key"
    server: idm-01.example.com
    kerberos_keytab: /etc/ipa/automation.keytab
    ipaadmin_principal: automation@EXAMPLE.COM
    verify: /etc/ipa/ca.crt
  no_log: true

- name: Remove private key from controller
  ansible.builtin.file:
    path: /tmp/app01-private.key
    state: absent

Why this pattern:


8. Idempotent Secret Provisioning in a Role

Roles that provision secrets should be safe to run repeatedly. Use state: archived with a standard vault to get the full idempotency guarantee: the role creates the vault on first run and only archives again if the payload changes.

# roles/app_secrets/tasks/main.yml

- name: Ensure app credentials vault exists and is populated
  eigenstate.ipa.vault_write:
    name: "{{ app_name }}-credentials"
    state: archived
    shared: "{{ vault_shared | default(true) }}"
    data: "{{ app_credentials | to_json }}"
    description: "Credentials for {{ app_name }}"
    server: "{{ ipa_server }}"
    kerberos_keytab: "{{ ipa_keytab }}"
    ipaadmin_principal: "{{ ipa_principal }}"
    verify: "{{ ipa_verify | default('/etc/ipa/ca.crt') }}"
  no_log: true
  register: credentials_vault

- name: Grant app service account access
  eigenstate.ipa.vault_write:
    name: "{{ app_name }}-credentials"
    state: present
    shared: "{{ vault_shared | default(true) }}"
    members: "{{ app_service_accounts | default([]) }}"
    server: "{{ ipa_server }}"
    kerberos_keytab: "{{ ipa_keytab }}"
    ipaadmin_principal: "{{ ipa_principal }}"
    verify: "{{ ipa_verify | default('/etc/ipa/ca.crt') }}"
  when: app_service_accounts | default([]) | length > 0

Why this pattern:


9. Symmetric Vault Creation and Password Rotation

Create a symmetric vault and rotate the symmetric encryption password. The password rotation requires delete-and-recreate because IdM does not expose a vault salt update operation. Data must be re-archived after the password changes.

- name: Provision symmetric vault
  hosts: localhost
  gather_facts: false

  vars:
    vault_name: encrypted-api-key
    ipa_server: idm-01.example.com
    ipa_keytab: /etc/ipa/automation.keytab
    ipa_principal: automation@EXAMPLE.COM

  tasks:
    - name: Create symmetric vault
      eigenstate.ipa.vault_write:
        name: "{{ vault_name }}"
        state: present
        vault_type: symmetric
        shared: true
        description: "API key with symmetric encryption"
        server: "{{ ipa_server }}"
        kerberos_keytab: "{{ ipa_keytab }}"
        ipaadmin_principal: "{{ ipa_principal }}"
        verify: /etc/ipa/ca.crt

    - name: Archive API key with current vault password
      eigenstate.ipa.vault_write:
        name: "{{ vault_name }}"
        state: archived
        shared: true
        data: "{{ api_key_value }}"
        vault_password: "{{ vault_password_current }}"
        server: "{{ ipa_server }}"
        kerberos_keytab: "{{ ipa_keytab }}"
        ipaadmin_principal: "{{ ipa_principal }}"
        verify: /etc/ipa/ca.crt
      no_log: true

Password rotation for a symmetric vault:

# Password rotation requires delete → recreate → archive
# because IdM has no vault_mod_password operation.

- name: Delete symmetric vault before password rotation
  eigenstate.ipa.vault_write:
    name: "{{ vault_name }}"
    state: absent
    shared: true
    server: "{{ ipa_server }}"
    kerberos_keytab: "{{ ipa_keytab }}"
    ipaadmin_principal: "{{ ipa_principal }}"
    verify: /etc/ipa/ca.crt

- name: Recreate symmetric vault with new salt
  eigenstate.ipa.vault_write:
    name: "{{ vault_name }}"
    state: present
    vault_type: symmetric
    shared: true
    description: "API key with symmetric encryption (rotated)"
    server: "{{ ipa_server }}"
    kerberos_keytab: "{{ ipa_keytab }}"
    ipaadmin_principal: "{{ ipa_principal }}"
    verify: /etc/ipa/ca.crt

- name: Archive data with new vault password
  eigenstate.ipa.vault_write:
    name: "{{ vault_name }}"
    state: archived
    shared: true
    data: "{{ api_key_value }}"
    vault_password: "{{ vault_password_new }}"
    server: "{{ ipa_server }}"
    kerberos_keytab: "{{ ipa_keytab }}"
    ipaadmin_principal: "{{ ipa_principal }}"
    verify: /etc/ipa/ca.crt
  no_log: true

[!NOTE] Symmetric vault password rotation is destructive: the vault is deleted and recreated with a new salt, then the data is re-archived under the new password. This is a write-window where the vault is briefly absent. Run this during a maintenance window or against a non-production vault first. The consuming automation must also update its vault password source before running the next retrieval.


10. Check-Mode Pre-Flight for Rotation Automation

Run the rotation play in check mode before execution to confirm what would change. This is especially useful in CI gates and change management workflows where a rotation job requires a pre-approval diff.

- name: Pre-flight check for secret rotation
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Generate candidate secret
      ansible.builtin.set_fact:
        candidate_secret: "{{ lookup('community.general.random_string',
                                     length=32, special=false) }}"
      no_log: true

    - name: Check what rotation would do (no writes)
      eigenstate.ipa.vault_write:
        name: app-secret
        state: archived
        shared: true
        data: "{{ candidate_secret }}"
        server: idm-01.example.com
        kerberos_keytab: /etc/ipa/automation.keytab
        ipaadmin_principal: automation@EXAMPLE.COM
        verify: /etc/ipa/ca.crt
      check_mode: true
      register: rotation_preview
      no_log: true

    - name: Report rotation impact
      ansible.builtin.debug:
        msg: >-
          Rotation would {{ 'update' if rotation_preview.changed else 'make no change to' }}
          vault 'app-secret' (scope: {{ rotation_preview.vault.scope }},
          type: {{ rotation_preview.vault.type }})

Why this pattern: