--- - name: Include vault variables ansible.builtin.include_vars: vault.yml - name: Get local public IP ansible.builtin.uri: url: https://ipinfo.io/ip return_content: true register: local_public_ip - name: Get public IP of "{{ domain }}" ansible.builtin.set_fact: target_public_ip: "{{ lookup('community.general.dig', domain, '@1.1.1.1') }}" - name: Create needed directories become: true ansible.builtin.file: path: "{{ item }}" state: directory owner: root group: root mode: 0755 with_items: - /etc/letsencrypt/renewal - /etc/letsencrypt/archive - "/etc/letsencrypt/archive/{{ domain }}" - /etc/letsencrypt/live - "/etc/letsencrypt/live/{{ domain }}" - name: Add webroot configuration for letsencrypt become: true ansible.builtin.lineinfile: path: /etc/letsencrypt/cli.ini line: webroot-path = /var/www/acme - name: Create renewal configuration for "{{ domain }}" become: true ansible.builtin.template: src: renewal.conf.j2 dest: "/etc/letsencrypt/renewal/{{ domain }}.conf" mode: 0644 - name: Create private key for account become: true community.crypto.openssl_privatekey_pipe: register: account_privkey - name: Create private key for challenge become: true community.crypto.openssl_privatekey: path: "/etc/letsencrypt/archive/{{ domain }}/privkey1.pem" register: challenge_privkey - name: Create csr for letsencrypt become: true community.crypto.openssl_csr_pipe: privatekey_path: "/etc/letsencrypt/archive/{{ domain }}/privkey1.pem" common_name: "{{ domain }}" register: csr changed_when: challenge_privkey is changed - name: Do http-01 challenge become: true when: local_public_ip.content == target_public_ip block: - name: Create acme challenge community.crypto.acme_certificate: acme_version: 2 acme_directory: https://acme-v02.api.letsencrypt.org/directory account_email: "{{ server_admin }}" account_key_content: "{{ account_privkey.privatekey }}" terms_agreed: true csr_content: "{{ csr.csr }}" challenge: http-01 dest: "/etc/letsencrypt/archive/{{ domain }}/cert1.pem" chain_dest: "/etc/letsencrypt/archive/{{ domain }}/chain1.pem" fullchain_dest: "/etc/letsencrypt/archive/{{ domain }}/fullchain1.pem" register: letsencrypt_challenge - name: Copy http-01 resource ansible.builtin.copy: dest: "/var/www/acme/{{ letsencrypt_challenge['challenge_data'][domain]['http-01']['resource'] }}" content: "{{ letsencrypt_challenge['challenge_data'][domain]['http-01']['resource_value'] }}" mode: 0644 when: letsencrypt_challenge is changed and domain in letsencrypt_challenge.challenge_data - name: Validate the challenge and get the cert community.crypto.acme_certificate: acme_version: 2 acme_directory: https://acme-v02.api.letsencrypt.org/directory account_email: "{{ server_admin }}" account_key_content: "{{ account_privkey.privatekey }}" csr_content: "{{ csr.csr }}" challenge: http-01 dest: "/etc/letsencrypt/archive/{{ domain }}/cert1.pem" chain_dest: "/etc/letsencrypt/archive/{{ domain }}/chain1.pem" fullchain_dest: "/etc/letsencrypt/archive/{{ domain }}/fullchain1.pem" data: "{{ letsencrypt_challenge }}" when: letsencrypt_challenge is changed and domain in letsencrypt_challenge.challenge_data - name: Remove the http-01 resource ansible.builtin.file: path: "/var/www/acme/{{ letsencrypt_challenge['challenge_data'][domain]['http-01']['resource'] }}" state: absent when: letsencrypt_challenge is changed and domain in letsencrypt_challenge.challenge_data - name: Do dns-01 challenge become: true when: local_public_ip.content != target_public_ip block: - name: Create acme challenge community.crypto.acme_certificate: acme_version: 2 acme_directory: https://acme-v02.api.letsencrypt.org/directory account_email: "{{ server_admin }}" account_key_content: "{{ account_privkey.privatekey }}" terms_agreed: true csr_content: "{{ csr.csr }}" challenge: dns-01 dest: "/etc/letsencrypt/archive/{{ domain }}/cert1.pem" chain_dest: "/etc/letsencrypt/archive/{{ domain }}/chain1.pem" fullchain_dest: "/etc/letsencrypt/archive/{{ domain }}/fullchain1.pem" register: letsencrypt_challenge - name: Create dns-01 record community.general.gandi_livedns: api_key: "{{ gandi_livedns_api_key }}" domain: "{{ (domain | split('.'))[-2:] | join('.') }}" record: "{{ letsencrypt_challenge.challenge_data[domain]['dns-01'].resource }}.{{ (domain | split('.'))[:-2] | join('.') }}" values: - "{{ letsencrypt_challenge.challenge_data[domain]['dns-01'].resource_value }}" type: TXT state: present ttl: 300 when: letsencrypt_challenge is changed and domain in letsencrypt_challenge.challenge_data - name: Wait for DNS to propagate ansible.builtin.pause: seconds: 300 when: letsencrypt_challenge is changed and domain in letsencrypt_challenge.challenge_data - name: Validate the challenge and get the cert community.crypto.acme_certificate: acme_version: 2 acme_directory: https://acme-v02.api.letsencrypt.org/directory account_email: "{{ server_admin }}" account_key_content: "{{ account_privkey.privatekey }}" csr_content: "{{ csr.csr }}" challenge: dns-01 dest: "/etc/letsencrypt/archive/{{ domain }}/cert1.pem" chain_dest: "/etc/letsencrypt/archive/{{ domain }}/chain1.pem" fullchain_dest: "/etc/letsencrypt/archive/{{ domain }}/fullchain1.pem" data: "{{ letsencrypt_challenge }}" when: letsencrypt_challenge is changed and domain in letsencrypt_challenge.challenge_data - name: Remove dns-01 record community.general.gandi_livedns: api_key: "{{ gandi_livedns_api_key }}" domain: "{{ (domain | split('.'))[-2:] | join('.') }}" record: "{{ letsencrypt_challenge.challenge_data[domain]['dns-01'].resource }}.{{ (domain | split('.'))[:-2] | join('.') }}" type: TXT state: absent when: letsencrypt_challenge is changed and domain in letsencrypt_challenge.challenge_data - name: Create symlinks for the certificate become: true ansible.builtin.file: path: "/etc/letsencrypt/live/{{ domain }}/{{ item.dest }}" src: "/etc/letsencrypt/archive/{{ domain }}/{{ item.src }}" state: link with_items: - {src: cert1.pem, dest: cert.pem} - {src: chain1.pem, dest: chain.pem} - {src: fullchain1.pem, dest: fullchain.pem} - {src: privkey1.pem, dest: privkey.pem}