# Buggy code
def _normalize_san(self, san):
    if san.startswith("IP Address:"):
        san = "IP:" + san[len("IP Address:") :]
    if san.startswith("IP:"):
        ip = ipaddress.ip_address(san[3:])
        san = "IP:{0}".format(ip.compressed)
    return san


# Traceback (nAttributeError)
root@test:/ansible/playbooks# ansible-playbook reproduce.yml

PLAY [localhost] *************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************
ok: [localhost]

TASK [Generate root CA private key] ******************************************************************************************************************************
ok: [localhost]

TASK [openssl_csr] ***********************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "/root/.ansible/tmp/ansible-tmp-1554976577.3779902-7265526702622/AnsiballZ_openssl_csr.py:18: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses\n  import imp\nTraceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1554976577.3779902-7265526702622/AnsiballZ_openssl_csr.py\", line 114, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1554976577.3779902-7265526702622/AnsiballZ_openssl_csr.py\", line 106, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1554976577.3779902-7265526702622/AnsiballZ_openssl_csr.py\", line 49, in invoke_module\n    imp.load_module('__main__', mod, module, MOD_DESC)\n  File \"/usr/local/lib/python3.7/imp.py\", line 234, in load_module\n    return load_source(name, filename, file)\n  File \"/usr/local/lib/python3.7/imp.py\", line 169, in load_source\n    module = _exec(spec, sys.modules[name])\n  File \"<frozen importlib._bootstrap>\", line 630, in _exec\n  File \"<frozen importlib._bootstrap_external>\", line 728, in exec_module\n  File \"<frozen importlib._bootstrap>\", line 219, in _call_with_frames_removed\n  File \"/tmp/ansible_openssl_csr_payload_agiegmf7/__main__.py\", line 924, in <module>\n  File \"/tmp/ansible_openssl_csr_payload_agiegmf7/__main__.py\", line 907, in main\n  File \"/tmp/ansible_openssl_csr_payload_agiegmf7/__main__.py\", line 441, in generate\n  File \"/tmp/ansible_openssl_csr_payload_agiegmf7/__main__.py\", line 469, in check\n  File \"/tmp/ansible_openssl_csr_payload_agiegmf7/__main__.py\", line 826, in _check_csr\n  File \"/tmp/ansible_openssl_csr_payload_agiegmf7/__main__.py\", line 803, in _check_extensions\n  File \"/tmp/ansible_openssl_csr_payload_agiegmf7/__main__.py\", line 764, in _check_basicConstraints\nAttributeError: 'Extension' object has no attribute 'ca'\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

PLAY RECAP *******************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

# Fixed code
def _normalize_san(self, san):
    # apperently openssl returns 'IP address' not 'IP' as specifier when converting the subjectAltName to string
    # although it won't accept this specifier when generating the CSR. (https://github.com/openssl/openssl/issues/4004)
    if san.startswith("IP Address:"):
        san = "IP:" + san[len("IP Address:") :]
    if san.startswith("IP:"):
        ip = ipaddress.ip_address(san[3:])
        san = "IP:{0}".format(ip.compressed)
    return san