eigenstate-ipa

Rotation Use Cases

Related docs:

  ROTATION CAPABILITIES     VAULT WRITE USE CASES     KEYTAB USE CASES     CERT USE CASES     AAP INTEGRATION     DOCS MAP  

Purpose

This page collects the rotation workflows that matter to operators evaluating eigenstate.ipa against Vault or CyberArk expectations.

These are not plugin-level references. They are controller-side workflow patterns built from the collection’s existing primitives.

Contents

1. Scheduled Static Secret Rotation In AAP

Use this when a shared application secret lives in an IdM vault and needs a periodic controller-driven update.

flowchart LR
    sched["AAP schedule"] --> gen["Generate new value"]
    gen --> archive["vault_write state=archived"]
    archive --> deploy["Deploy or inject new value"]
    deploy --> validate["Validate application"]

Example:

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

  vars:
    ipa_server: idm-01.example.com
    ipa_keytab: /runner/env/ipa/admin.keytab

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

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

    - name: Report whether the secret changed
      ansible.builtin.debug:
        msg: "Secret rotation changed={{ rotation_result.changed }}"

Why this is the preferred shape:

2. Pre-Approved Secret Rotation With Check Mode

Use this when an approval gate needs a preview before the actual rotation run.

flowchart LR
    preview["check_mode preview"] --> approve{"approved?"}
    approve -->|yes| run["real archive run"]
    approve -->|no| stop["no write"]

Example:

- name: Preview static 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: Preview archive result
      eigenstate.ipa.vault_write:
        name: app-secret
        state: archived
        shared: true
        data: "{{ candidate_secret }}"
        server: idm-01.example.com
        kerberos_keytab: /runner/env/ipa/admin.keytab
        verify: /etc/ipa/ca.crt
      check_mode: true
      register: rotation_preview
      no_log: true

    - name: Show preview outcome
      ansible.builtin.debug:
        msg: "Rotation would change={{ rotation_preview.changed }}"

This is the closest thing the collection has to a dry-run rotation engine, and it is intentionally tied to the real module semantics rather than a synthetic planner layer.

3. Coordinated Keytab Rotation For A Shared Service Principal

Use this when a service principal is shared by one or more hosts and the new keytab must be deployed immediately after rotation.

flowchart LR
    pre["principal pre-flight"] --> rotate["keytab retrieve_mode='generate'"]
    rotate --> deploy["copy new keytab to all consumers"]
    deploy --> restart["restart or reload services"]

Example:

- name: Rotate and redeploy shared HTTP keytab
  hosts: webservers
  gather_facts: false

  vars:
    principal: HTTP/web.example.com

  tasks:
    - name: Confirm principal is ready
      ansible.builtin.set_fact:
        principal_state: "{{ lookup('eigenstate.ipa.principal',
                              principal,
                              server='idm-01.example.com',
                              kerberos_keytab='/runner/env/ipa/admin.keytab',
                              result_format='record',
                              verify='/etc/ipa/ca.crt') }}"
      delegate_to: localhost
      run_once: true

    - name: Fail if the principal is not ready
      ansible.builtin.assert:
        that:
          - principal_state.exists
          - principal_state.has_keytab
      delegate_to: localhost
      run_once: true

    - name: Rotate keytab on the controller
      ansible.builtin.set_fact:
        rotated_keytab_b64: "{{ lookup('eigenstate.ipa.keytab',
                                  principal,
                                  server='idm-01.example.com',
                                  kerberos_keytab='/runner/env/ipa/admin.keytab',
                                  retrieve_mode='generate',
                                  verify='/etc/ipa/ca.crt') }}"
      delegate_to: localhost
      run_once: true
      no_log: true

    - name: Deploy rotated keytab
      ansible.builtin.copy:
        content: "{{ hostvars[groups['webservers'][0]].rotated_keytab_b64 | b64decode }}"
        dest: /etc/httpd/conf/httpd.keytab
        mode: "0600"
        owner: apache
        group: apache
      no_log: true

    - name: Reload httpd
      ansible.builtin.systemd:
        name: httpd
        state: reloaded

This pattern is intentionally explicit because keytab rotation is destructive. If the rotation and deploy phases are separated, the collection cannot save the service from holding an invalidated keytab.

4. Certificate Renewal From Expiry Discovery

Use this when certificate rotation is driven by an expiry window rather than a native cert lease model.

flowchart LR
    find["cert operation=find"] --> loop["loop over expiring certs"]
    loop --> request["cert operation=request"]
    request --> deploy["deploy renewed cert"]

Example:

- name: Renew expiring certificates
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Find certificates expiring soon
      ansible.builtin.set_fact:
        expiring_certs: "{{ lookup('eigenstate.ipa.cert',
                              operation='find',
                              server='idm-01.example.com',
                              kerberos_keytab='/runner/env/ipa/admin.keytab',
                              valid_not_after_to='2026-06-05',
                              result_format='map_record',
                              verify='/etc/ipa/ca.crt') }}"

    - name: Renew each certificate from its matching CSR workflow
      ansible.builtin.debug:
        msg: "Renew certificate with serial {{ item.key }} for {{ item.value.metadata.subject }}"
      loop: "{{ expiring_certs | dict2items }}"

The renewal trigger here is explicit and query-driven. That is the collection’s PKI rotation model.

5. Rotation Positioning For Platform Teams

Use this wording when a team asks whether the collection “has rotation”:

That positioning is strong enough for the collection and honest enough to publish.

For the underlying decision model, return to ROTATION CAPABILITIES.