Introduction

The community.crypto collection provides Ansible modules for complete PKI management — generate private keys, create CSRs, sign certificates, build certificate authorities, manage certificate chains, and check expiry. No need for manual openssl commands; everything is declarative and idempotent.

Prerequisites

ansible-galaxy collection install community.crypto
pip install cryptography

Module Reference

ModulePurpose
openssl_privatekeyGenerate RSA/EC private keys
openssl_csrCreate Certificate Signing Requests
x509_certificateGenerate/sign certificates
x509_certificate_infoRead certificate details
certificate_complete_chainBuild certificate chains
openssl_pkcs12Create PKCS12 keystores

Generate Private Key

- name: Generate RSA private key
  community.crypto.openssl_privatekey:
    path: /etc/ssl/private/server.key
    size: 4096
    type: RSA
    mode: '0600'

- name: Generate EC private key (faster, smaller)
  community.crypto.openssl_privatekey:
    path: /etc/ssl/private/server-ec.key
    type: ECC
    curve: secp384r1
    mode: '0600'

Create CSR

- name: Generate CSR
  community.crypto.openssl_csr:
    path: /etc/ssl/csr/server.csr
    privatekey_path: /etc/ssl/private/server.key
    common_name: www.example.com
    organization_name: Example Corp
    organizational_unit_name: IT
    country_name: US
    state_or_province_name: California
    locality_name: San Francisco
    email_address: admin@example.com
    subject_alt_name:
      - "DNS:www.example.com"
      - "DNS:example.com"
      - "DNS:api.example.com"
      - "IP:203.0.113.10"
    key_usage:
      - digitalSignature
      - keyEncipherment
    extended_key_usage:
      - serverAuth
      - clientAuth

Self-Signed Certificate

- name: Generate self-signed certificate
  community.crypto.x509_certificate:
    path: /etc/ssl/certs/server.crt
    privatekey_path: /etc/ssl/private/server.key
    csr_path: /etc/ssl/csr/server.csr
    provider: selfsigned
    selfsigned_not_after: "+365d"
    mode: '0644'

Build a Certificate Authority

Root CA

---
- name: Create Certificate Authority
  hosts: ca_server
  become: true
  vars:
    ca_dir: /etc/pki/ca
  tasks:
    - name: Create CA directories
      ansible.builtin.file:
        path: "{{ ca_dir }}/{{ item }}"
        state: directory
        mode: '0700'
      loop: [private, certs, csr, newcerts]

    - name: Generate CA private key
      community.crypto.openssl_privatekey:
        path: "{{ ca_dir }}/private/ca.key"
        size: 4096
        mode: '0400'
        passphrase: "{{ vault_ca_passphrase }}"
        cipher: auto

    - name: Generate CA CSR
      community.crypto.openssl_csr:
        path: "{{ ca_dir }}/csr/ca.csr"
        privatekey_path: "{{ ca_dir }}/private/ca.key"
        privatekey_passphrase: "{{ vault_ca_passphrase }}"
        common_name: "Example Corp Root CA"
        organization_name: "Example Corp"
        country_name: US
        basic_constraints:
          - "CA:TRUE"
          - "pathlen:1"
        basic_constraints_critical: true
        key_usage:
          - keyCertSign
          - cRLSign
        key_usage_critical: true

    - name: Self-sign CA certificate
      community.crypto.x509_certificate:
        path: "{{ ca_dir }}/certs/ca.crt"
        privatekey_path: "{{ ca_dir }}/private/ca.key"
        privatekey_passphrase: "{{ vault_ca_passphrase }}"
        csr_path: "{{ ca_dir }}/csr/ca.csr"
        provider: selfsigned
        selfsigned_not_after: "+3650d"
        mode: '0644'

Sign Server Certificates

- name: Sign certificate with CA
  community.crypto.x509_certificate:
    path: /etc/ssl/certs/server.crt
    csr_path: /etc/ssl/csr/server.csr
    ownca_path: "{{ ca_dir }}/certs/ca.crt"
    ownca_privatekey_path: "{{ ca_dir }}/private/ca.key"
    ownca_privatekey_passphrase: "{{ vault_ca_passphrase }}"
    ownca_not_after: "+365d"
    provider: ownca
    mode: '0644'

Distribute CA Certificate

- name: Distribute CA certificate to all servers
  hosts: all
  become: true
  tasks:
    - name: Copy CA cert
      ansible.builtin.copy:
        src: "{{ ca_dir }}/certs/ca.crt"
        dest: /usr/local/share/ca-certificates/example-ca.crt
        mode: '0644'

    - name: Update CA trust store
      ansible.builtin.command: update-ca-certificates
      changed_when: true

Check Certificate Expiry

- name: Check certificate info
  community.crypto.x509_certificate_info:
    path: /etc/ssl/certs/server.crt
  register: cert_info

- name: Display certificate details
  ansible.builtin.debug:
    msg: |
      Subject: {{ cert_info.subject }}
      Issuer: {{ cert_info.issuer }}
      Not After: {{ cert_info.not_after }}
      SANs: {{ cert_info.subject_alt_name }}

- name: Alert if expiring within 30 days
  ansible.builtin.assert:
    that:
      - cert_info.not_after is community.crypto.x509_certificate_info
    fail_msg: "Certificate expires soon: {{ cert_info.not_after }}"
  ignore_errors: true

Fleet-Wide Certificate Audit

---
- name: Audit all TLS certificates
  hosts: all
  become: true
  tasks:
    - name: Find all certificates
      ansible.builtin.find:
        paths:
          - /etc/ssl/certs
          - /etc/nginx/ssl
          - /etc/haproxy/certs
        patterns: "*.crt,*.pem"
        recurse: true
      register: cert_files

    - name: Check each certificate
      community.crypto.x509_certificate_info:
        path: "{{ item.path }}"
      loop: "{{ cert_files.files }}"
      register: all_certs

    - name: Report expiring certificates
      ansible.builtin.debug:
        msg: "⚠️ {{ item.item.path }} expires {{ item.not_after }}"
      loop: "{{ all_certs.results }}"
      when: item.not_after is defined

PKCS12 Keystore (Java/Tomcat)

- name: Create PKCS12 keystore
  community.crypto.openssl_pkcs12:
    action: export
    path: /etc/ssl/keystore.p12
    privatekey_path: /etc/ssl/private/server.key
    certificate_path: /etc/ssl/certs/server.crt
    other_certificates:
      - /etc/ssl/certs/ca.crt
    passphrase: "{{ vault_keystore_password }}"
    friendly_name: server
    mode: '0600'

Certificate Chain

- name: Build complete certificate chain
  ansible.builtin.assemble:
    src: /etc/ssl/chain-parts/
    dest: /etc/ssl/certs/fullchain.pem
    mode: '0644'
  # Directory should contain: server.crt, intermediate.crt, root.crt

Troubleshooting

Verify Certificate Matches Key

- name: Get certificate modulus
  ansible.builtin.command: openssl x509 -noout -modulus -in /etc/ssl/certs/server.crt
  register: cert_modulus
  changed_when: false

- name: Get key modulus
  ansible.builtin.command: openssl rsa -noout -modulus -in /etc/ssl/private/server.key
  register: key_modulus
  changed_when: false

- name: Verify match
  ansible.builtin.assert:
    that: cert_modulus.stdout == key_modulus.stdout
    fail_msg: "Certificate and key do not match!"

Conclusion

The community.crypto collection makes PKI management fully declarative — generate keys, create CSRs, build CAs, sign certificates, check expiry, and create keystores without a single openssl command. Use self-signed certs for internal services, your own CA for fleet-wide trust, and Let's Encrypt for public-facing services. Certificate management as code means every cert is tracked, versioned, and reproducible.