Answers: <p>There were a couple mistakes in my tasks, but this one works:</p>
  <pre class="lang-yaml prettyprint-override"><code>---
  
  # Create the directories required by letsencrypt to store keys and certificates.
  - name: &quot;Create required directories in /etc/letsencrypt&quot;
    become: yes
    file:
      path: &quot;/etc/letsencrypt/{{ item }}&quot;
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=x,o=x
    with_items:
      - account
      - certs
      - csrs
      - keys
  
  # https://docs.ansible.com/ansible/2.9/modules/acme_certificate_module.html#acme-certificate-module
  - name: Generate let's encrypt account key
    become: yes
    openssl_privatekey:
      path: &quot;{{ letsencrypt_account_key }}&quot;
  
  # https://docs.ansible.com/ansible/latest/collections/community/crypto/openssl_privatekey_module.html#openssl-privatekey-module
  - name: Generate let's encrypt private key with the default values (4096 bits, RSA)
    become: yes
    openssl_privatekey:
      path: &quot;{{letsencrypt_keys_dir}}/{{ domain_name }}.key&quot;
  
  # https://docs.ansible.com/ansible/latest/collections/community/crypto/openssl_csr_module.html#openssl-csr-module
  - name: Generate an OpenSSL Certificate Signing Request
    become: yes
    community.crypto.openssl_csr:
      path: &quot;{{letsencrypt_csrs_dir}}/{{ domain_name }}.csr&quot;
      privatekey_path: &quot;{{letsencrypt_keys_dir}}/{{ domain_name }}.key&quot;
      common_name: &quot;{{domain_name}}&quot;
  
  # Create letsencrypt challenge.
  - name: Create a challenge for {{domain_name}} using a account key file.
    become: yes
    community.crypto.acme_certificate:
      acme_directory: &quot;{{acme_directory}}&quot;
      acme_version: &quot;{{acme_version}}&quot;
      account_email: &quot;{{acme_email}}&quot;
      terms_agreed: yes
      account_key_src: &quot;{{letsencrypt_account_key}}&quot;
      csr: &quot;{{letsencrypt_csrs_dir}}/{{domain_name}}.csr&quot;
      dest: &quot;{{letsencrypt_certs_dir}}/{{domain_name}}.crt&quot;
      remaining_days: &quot;{{remaining_days}}&quot;
    register: acme_challenge
  
  # Create the directory to hold the validation token.
  - name: &quot;Create .well-known/acme-challenge directory&quot;
    become: yes
    file:
      path: &quot;{{project_path}}/.well-known/acme-challenge&quot;
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=rx,o=rx
  
  # Copy the necessary files for the http-01 challenge.
  - name: &quot;Implement http-01 challenge files&quot;
    become: yes
    copy:
      dest: &quot;{{project_path}}/{{ acme_challenge['challenge_data'][item]['http-01']['resource'] }}&quot;
      content: &quot;{{ acme_challenge['challenge_data'][item]['http-01']['resource_value'] }}&quot;
    with_items:
      - &quot;{{ domain_name }}&quot;
    when: acme_challenge is changed and domain_name|string in acme_challenge['challenge_data']
  
  # Execute letsencrypt challenge.
  - name: Let the challenge be validated and retrieve the cert and intermediate certificate
    become: yes
    community.crypto.acme_certificate:
      account_key_src: &quot;{{letsencrypt_account_key}}&quot;
      csr: &quot;{{letsencrypt_csrs_dir}}/{{domain_name}}.csr&quot;
      cert: &quot;{{letsencrypt_certs_dir}}/{{domain_name}}.crt&quot;
      acme_directory: &quot;{{acme_directory}}&quot;
      acme_version: &quot;{{acme_version}}&quot;
      account_email: &quot;{{acme_email}}&quot;
      challenge: &quot;{{acme_challenge_type}}&quot;
      fullchain: &quot;{{letsencrypt_certs_dir}}/{{domain_name}}-fullchain.crt&quot;
      chain: &quot;{{letsencrypt_certs_dir}}/{{domain_name}}-intermediate.crt&quot;
      remaining_days: &quot;{{remaining_days}}&quot;
      data: &quot;{{ acme_challenge }}&quot;
    when: acme_challenge is changed
  </code></pre>
  <p>The corresponding Nginx config file is:</p>
  <pre><code>upstream {{project_name}} {
      server unix:{{socket_location}};
  }
  
  server {
      listen 80;
      location /.well-known/acme-challenge/ {
          root {{project_path}};
      }
      server_name {{domain_name}} www.{{domain_name}} {{web_server_ip}};
      return 301 https://{{domain_name}}$request_uri;
  }
  
  server {
  
      listen 443 ssl;
      ssl_certificate /etc/letsencrypt/certs/{{domain_name}}-fullchain.crt;
      ssl_certificate_key /etc/letsencrypt/keys/{{domain_name}}.key;
  
      charset utf-8;
      client_max_body_size 4M;
  
      # Serving static files directly from Nginx without passing through uwsgi 
      location /app/static/ {
          alias {{project_path}}/app/static/;
      }
  
      location /.well-known/acme-challenge/ {
          root {{project_path}};
      }
  
      location / {
          # kill cache
          add_header Last-Modified $date_gmt;
          add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
          if_modified_since off;
          expires off;
          etag off;
          uwsgi_pass {{project_name}};
          include {{project_path}}/uwsgi_params;
      }
  }
  </code></pre>
  
Answers: CC BY-SA 4.0
Answers: 2022-06-14T21:09:50.877Z
Answers: 2022-07-15T06:54:14.817Z
Answers: 2022-07-15T06:54:14.817Z
Answers: Tanuki
Body: <p>I'm trying to get a let's encrypt certificate for my domain with Ansible.
  I have been reading <a href="https://www.digitalocean.com/community/tutorials/how-to-acquire-a-let-s-encrypt-certificate-using-ansible-on-ubuntu-18-04" rel="nofollow noreferrer">this tutorial</a> which is a bit outdated and <a href="https://docs.ansible.com/ansible/latest/collections/community/crypto/acme_certificate_module.html#ansible-collections-community-crypto-acme-certificate-module" rel="nofollow noreferrer">the Ansible documentation</a>.</p>
  <p>My playbook is a mix of what I have found in the tutorial mentioned and the documentation.</p>
  <pre class="lang-yaml prettyprint-override"><code>---
  
  - name: &quot;Create required directories in /etc/letsencrypt&quot;
    file:
      path: &quot;/etc/letsencrypt/{{ item }}&quot;
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=x,o=x
    with_items:
      - account
      - certs
      - csrs
      - keys
  
  - name: Generate let's encrypt account key
    openssl_privatekey:
      path: &quot;/etc/letsencrypt/account/account.key&quot;
  
  - name: Generate let's encrypt private key with the default values (4096 bits, RSA)
    openssl_privatekey:
      path: &quot;/etc/letsencrypt/keys/domain.me.key&quot;
  
  - name: Generate an OpenSSL Certificate Signing Request
    community.crypto.openssl_csr:
      path: &quot;/etc/letsencrypt/csrs/domain.me.csr&quot;
      privatekey_path: &quot;/etc/letsencrypt/keys/domain.me.key&quot;
      common_name: www.domain.me
  
  # Create challenge
  - name: Create a challenge for domain.me using an account key file.
    acme_certificate:
      acme_directory: &quot;https://acme-v02.api.letsencrypt.org/directory&quot;
      acme_version: 2
      account_key_src: &quot;/etc/letsencrypt/account/account.key&quot;
      account_email: &quot;email@mail.com&quot;
      terms_agreed: yes
      challenge: &quot;http-01&quot;
      src: &quot;/etc/letsencrypt/csrs/domain.me.csr&quot;
      dest: &quot;/etc/letsencrypt/certs/domain.me.crt&quot;
      fullchain_dest: &quot;/etc/letsencrypt/certs/domain.me-fullchain.crt&quot;
    register: acme_challenge_domain_me
  
  - name: &quot;Create .well-known/acme-challenge directory&quot;
    file:
      path: &quot;project/dir/path/.well-known/acme-challenge&quot;
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=rx,o=rx
  
  - name: &quot;Implement http-01 challenge files&quot;
    copy:
      content: &quot;{{ acme_challenge_domain_me['challenge_data'][item]['http-01']['resource_value'] }}&quot;
      dest: &quot;project/dir/path/{{ acme_challenge_domain_me['challenge_data'][item]['http-01']['resource'] }}&quot;
    with_items:
      - &quot;domain.me&quot;
      - &quot;www.domain.me&quot;
    when: acme_challenge_domain_me is changed and domain_name|string in acme_challenge_domain_me['challenge_data']
  
  - name: Let the challenge be validated and retrieve the cert and intermediate certificate
    acme_certificate:
      acme_directory: &quot;https://acme-v02.api.letsencrypt.org/directory&quot;
      acme_version: 2
      account_key_src: &quot;/etc/letsencrypt/account/account.key&quot;
      account_email: &quot;email@mail.com&quot;
      challenge: &quot;http-01&quot;
      src: &quot;/etc/letsencrypt/csrs/domain.me.csr&quot;
      cert: &quot;/etc/letsencrypt/certs/domain.me.crt&quot;
      fullchain: &quot;/etc/letsencrypt/certs/domain.me-fullchain.crt&quot;
      chain: &quot;{/etc/letsencrypt/certs/domain.me-intermediate.crt&quot;
      remaining_days: &quot;60&quot;
      data: &quot;{{ acme_challenge_domain_me }}&quot;
    when: acme_challenge_domain_me is changed
  
  </code></pre>
  <p>When I run the playbook, I'm getting this error:</p>
  <p><code>fatal: [web_server]: FAILED! =&gt;</code></p>
  <pre class="lang-json prettyprint-override"><code>{
    &quot;changed&quot;: false,
    &quot;msg&quot;: &quot;Failed to validate challenge for dns:www.domain.me: Status is \&quot;invalid\&quot;. Challenge http-01: Error urn:ietf:params:acme:error:connection: \&quot;xxx.xxx.x.ip: Fetching http://www.domain.me/.well-known/acme-challenge/NRkTQSpAVbWtjFNq206YES55lEoHHinHUn9cjR7vm7k: Connection refused\&quot;.&quot;,
    &quot;other&quot;: {
      &quot;authorization&quot;: {
        &quot;challenges&quot;: [
          {
            &quot;error&quot;: {
              &quot;detail&quot;: &quot;xxx.xxx.x.ip: Fetching http://www.domain.me/.well-known/acme-challenge/NRkTQSpAVbWtjFNq206YES55lEoHHinHUn9cjR7vm7k: Connection refused&quot;,
              &quot;status&quot;: 400,
              &quot;type&quot;: &quot;urn:ietf:params:acme:error:connection&quot;
            },
            &quot;status&quot;: &quot;invalid&quot;,
            &quot;token&quot;: &quot;NRkTQSpAVbWtjFNq206YES55lEoHHinHUn9cjR7vm7k&quot;, 
            &quot;type&quot;: &quot;http-01&quot;,
            &quot;url&quot;: &quot;https://acme-v02.api.letsencrypt.org/acme/chall-v3/103702154687/UdA36w&quot;,
            &quot;validated&quot;: &quot;2022-04-30T16:01:32Z&quot;,
            &quot;validationRecord&quot;: [
              {
                &quot;addressUsed&quot;: &quot;xxx.xxx.x.ip&quot;,
                &quot;addressesResolved&quot;: [&quot;xxx.xxx.x.ip&quot;],
                &quot;hostname&quot;: &quot;www.domain.me&quot;,
                &quot;port&quot;: &quot;80&quot;,
                &quot;url&quot;: &quot;http://www.domain.me/.well-known/acme-challenge/NRkTQSpAVbWtjFNq206YES55lEoHHinHUn9cjR7vm7k&quot;
              }
            ]
          }
        ],
        &quot;expires&quot;: &quot;2022-05-07T15:57:28Z&quot;,
        &quot;identifier&quot;: {
          &quot;type&quot;: &quot;dns&quot;,
          &quot;value&quot;: &quot;www.domain.me&quot;
        },
        &quot;status&quot;: &quot;invalid&quot;, 
        &quot;uri&quot;: &quot;https://acme-v02.api.letsencrypt.org/acme/authz-v3/103702154687&quot;},
        &quot;identifier&quot;: &quot;dns:www.domain.me&quot;
      }
    }
  </code></pre>
  <p>Command UFW status gives:</p>
  <pre class="lang-none prettyprint-override"><code>To                         Action      From
  --                         ------      ----
  OpenSSH                    ALLOW       Anywhere
  80                         ALLOW       Anywhere
  5432/tcp                   ALLOW       Anywhere
  443/tcp                    ALLOW       Anywhere
  OpenSSH (v6)               ALLOW       Anywhere (v6)
  80 (v6)                    ALLOW       Anywhere (v6)
  5432/tcp (v6)              ALLOW       Anywhere (v6)
  443/tcp (v6)               ALLOW       Anywhere (v6)
  </code></pre>
  <p>The nginx configuration is :</p>
  <pre><code>upstream project {
      server unix:///tmp/project.sock;
  }
  
  server {
      listen 443 ssl;
      server_name www.domain.me;
      ssl_certificate /etc/letsencrypt/certs/domain.me.crt;
      ssl_certificate_key /etc/letsencrypt/keys/domain.me.key;
  
      listen 80;
      server_name domain.me www.domain.me;
      charset utf-8;
      client_max_body_size 4M;
      return 302 https://$server_name$request_uri;
  
      # Serving static files directly from Nginx without passing through uwsgi
      location /app/static/ {
          alias /home/admin/project/app/static/;
      }
  
      location / {
          # kill cache
          add_header Last-Modified $date_gmt;
          add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
          if_modified_since off;
          expires off;
          etag off;
          uwsgi_pass project;
          include /home/admin/project/uwsgi_params;
      }
  
      #location /404 {
      #   uwsgi_pass project;
      #   include /home/admin/project/uwsgi_params;
      #}
  }
  </code></pre>
  <p>Could you help me understand where the problem is coming from and how to solve it?</p>
  <p>I'm not sure if my mistakes are coming from the playbook, Nginx settings, or somewhere else, so apologize if the question isn't perfectly targeted.
  It's my first time doing this, so please include details and explanations to help me understand.</p>
  <p>Thank you.</p>
  
Comments: CC BY-SA 4.0
Comments: 2022-06-04T07:36:21.257Z
Comments: You are redirecting every singlle call on http to its https counterpart. A bit of a shortcut here but in a common default setup this basically can't work as let's encrypts needs access over unencrypted http to get the challenge files. Moreover, I don't see any configuration in nginx to allow acessing `/.well-known/acme-challenge/*` files (unless you already tested this works from you app folder....). In a typical let's encrypt setup, you allow accessing the `/.well-known` path (and all its subdirs) over http and redirect the rest to https.
Comments: Zeitounator
Comments: CC BY-SA 4.0
Comments: 2022-06-14T21:12:22.717Z
Comments: @Zeitounator yes, I was missing the /.well-known/acme-challenge/ and there were a couple of mistakes in my task.
Comments: Tanuki
Tags: <ansible><nginx><letsencrypt>
Title: How can I set let's encrypt certificates with Ansible?
UserDisplayName: Tanuki
ThreadText: # How can I set let's encrypt certificates with Ansible?
  
  (Asked by: username_0 on 2022-04-30)
  
  I'm trying to get a let's encrypt certificate for my domain with Ansible.
  I have been reading [this tutorial](https://www.digitalocean.com/community/tutorials/how-to-acquire-a-let-s-encrypt-certificate-using-ansible-on-ubuntu-18-04) which is a bit outdated and [the Ansible documentation](https://docs.ansible.com/ansible/latest/collections/community/crypto/acme_certificate_module.html#ansible-collections-community-crypto-acme-certificate-module).
  
  My playbook is a mix of what I have found in the tutorial mentioned and the documentation.
  
  ```yaml
  ---
  
  - name: "Create required directories in /etc/letsencrypt"
    file:
      path: "/etc/letsencrypt/{{ item }}"
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=x,o=x
    with_items:
      - account
      - certs
      - csrs
      - keys
  
  - name: Generate let's encrypt account key
    openssl_privatekey:
      path: "/etc/letsencrypt/account/account.key"
  
  - name: Generate let's encrypt private key with the default values (4096 bits, RSA)
    openssl_privatekey:
      path: "/etc/letsencrypt/keys/domain.me.key"
  
  - name: Generate an OpenSSL Certificate Signing Request
    community.crypto.openssl_csr:
      path: "/etc/letsencrypt/csrs/domain.me.csr"
      privatekey_path: "/etc/letsencrypt/keys/domain.me.key"
      common_name: www.domain.me
  
  # Create challenge
  - name: Create a challenge for domain.me using an account key file.
    acme_certificate:
      acme_directory: "https://acme-v02.api.letsencrypt.org/directory"
      acme_version: 2
      account_key_src: "/etc/letsencrypt/account/account.key"
      account_email: "email@mail.com"
      terms_agreed: yes
      challenge: "http-01"
      src: "/etc/letsencrypt/csrs/domain.me.csr"
      dest: "/etc/letsencrypt/certs/domain.me.crt"
      fullchain_dest: "/etc/letsencrypt/certs/domain.me-fullchain.crt"
    register: acme_challenge_domain_me
  
  - name: "Create .well-known/acme-challenge directory"
    file:
      path: "project/dir/path/.well-known/acme-challenge"
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=rx,o=rx
  
  - name: "Implement http-01 challenge files"
    copy:
      content: "{{ acme_challenge_domain_me['challenge_data'][item]['http-01']['resource_value'] }}"
      dest: "project/dir/path/{{ acme_challenge_domain_me['challenge_data'][item]['http-01']['resource'] }}"
    with_items:
      - "domain.me"
      - "www.domain.me"
    when: acme_challenge_domain_me is changed and domain_name|string in acme_challenge_domain_me['challenge_data']
  
  - name: Let the challenge be validated and retrieve the cert and intermediate certificate
    acme_certificate:
      acme_directory: "https://acme-v02.api.letsencrypt.org/directory"
      acme_version: 2
      account_key_src: "/etc/letsencrypt/account/account.key"
      account_email: "email@mail.com"
      challenge: "http-01"
      src: "/etc/letsencrypt/csrs/domain.me.csr"
      cert: "/etc/letsencrypt/certs/domain.me.crt"
      fullchain: "/etc/letsencrypt/certs/domain.me-fullchain.crt"
      chain: "{/etc/letsencrypt/certs/domain.me-intermediate.crt"
      remaining_days: "60"
      data: "{{ acme_challenge_domain_me }}"
    when: acme_challenge_domain_me is changed
  
  ```
  
  When I run the playbook, I'm getting this error:
  
  `fatal: [web_server]: FAILED! =>`
  
  ```json
  {
    "changed": false,
    "msg": "Failed to validate challenge for dns:www.domain.me: Status is \"invalid\". Challenge http-01: Error urn:ietf:params:acme:error:connection: \"xxx.xxx.x.ip: Fetching http://www.domain.me/.well-known/acme-challenge/NRkTQSpAVbWtjFNq206YES55lEoHHinHUn9cjR7vm7k: Connection refused\".",
    "other": {
      "authorization": {
        "challenges": [
          {
            "error": {
              "detail": "xxx.xxx.x.ip: Fetching http://www.domain.me/.well-known/acme-challenge/NRkTQSpAVbWtjFNq206YES55lEoHHinHUn9cjR7vm7k: Connection refused",
              "status": 400,
              "type": "urn:ietf:params:acme:error:connection"
            },
            "status": "invalid",
            "token": "NRkTQSpAVbWtjFNq206YES55lEoHHinHUn9cjR7vm7k", 
            "type": "http-01",
            "url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/103702154687/UdA36w",
            "validated": "2022-04-30T16:01:32Z",
            "validationRecord": [
              {
                "addressUsed": "xxx.xxx.x.ip",
                "addressesResolved": ["xxx.xxx.x.ip"],
                "hostname": "www.domain.me",
                "port": "80",
                "url": "http://www.domain.me/.well-known/acme-challenge/NRkTQSpAVbWtjFNq206YES55lEoHHinHUn9cjR7vm7k"
              }
            ]
          }
        ],
        "expires": "2022-05-07T15:57:28Z",
        "identifier": {
          "type": "dns",
          "value": "www.domain.me"
        },
        "status": "invalid", 
        "uri": "https://acme-v02.api.letsencrypt.org/acme/authz-v3/103702154687"},
        "identifier": "dns:www.domain.me"
      }
    }
  
  ```
  
  Command UFW status gives:
  
  ```none
  To                         Action      From
  --                         ------      ----
  OpenSSH                    ALLOW       Anywhere
  80                         ALLOW       Anywhere
  5432/tcp                   ALLOW       Anywhere
  443/tcp                    ALLOW       Anywhere
  OpenSSH (v6)               ALLOW       Anywhere (v6)
  80 (v6)                    ALLOW       Anywhere (v6)
  5432/tcp (v6)              ALLOW       Anywhere (v6)
  443/tcp (v6)               ALLOW       Anywhere (v6)
  
  ```
  
  The nginx configuration is :
  
  ```
  upstream project {
      server unix:///tmp/project.sock;
  }
  
  server {
      listen 443 ssl;
      server_name www.domain.me;
      ssl_certificate /etc/letsencrypt/certs/domain.me.crt;
      ssl_certificate_key /etc/letsencrypt/keys/domain.me.key;
  
      listen 80;
      server_name domain.me www.domain.me;
      charset utf-8;
      client_max_body_size 4M;
      return 302 https://$server_name$request_uri;
  
      # Serving static files directly from Nginx without passing through uwsgi
      location /app/static/ {
          alias /home/admin/project/app/static/;
      }
  
      location / {
          # kill cache
          add_header Last-Modified $date_gmt;
          add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
          if_modified_since off;
          expires off;
          etag off;
          uwsgi_pass project;
          include /home/admin/project/uwsgi_params;
      }
  
      #location /404 {
      #   uwsgi_pass project;
      #   include /home/admin/project/uwsgi_params;
      #}
  }
  
  ```
  
  Could you help me understand where the problem is coming from and how to solve it?
  
  I'm not sure if my mistakes are coming from the playbook, Nginx settings, or somewhere else, so apologize if the question isn't perfectly targeted.
  It's my first time doing this, so please include details and explanations to help me understand.
  
  Thank you.
  
  **Tags:** ansible, nginx, letsencrypt
  
  > **Comments:**
  > **username_1**: You are redirecting every singlle call on http to its https counterpart. A bit of a shortcut here but in a common default setup this basically can't work as let's encrypts needs access over unencrypted http to get the challenge files. Moreover, I don't see any configuration in nginx to allow acessing `/.well-known/acme-challenge/*` files (unless you already tested this works from you app folder....). In a typical let's encrypt setup, you allow accessing the `/.well-known` path (and all its subdirs) over http and redirect the rest to https.  
  > **username_0**: @username_1 yes, I was missing the /.well-known/acme-challenge/ and there were a couple of mistakes in my task.  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_0 on 2022-06-14. Score: 1
  
  There were a couple mistakes in my tasks, but this one works:
  
  ```yaml
  ---
  
  # Create the directories required by letsencrypt to store keys and certificates.
  - name: "Create required directories in /etc/letsencrypt"
    become: yes
    file:
      path: "/etc/letsencrypt/{{ item }}"
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=x,o=x
    with_items:
      - account
      - certs
      - csrs
      - keys
  
  # https://docs.ansible.com/ansible/2.9/modules/acme_certificate_module.html#acme-certificate-module
  - name: Generate let's encrypt account key
    become: yes
    openssl_privatekey:
      path: "{{ letsencrypt_account_key }}"
  
  # https://docs.ansible.com/ansible/latest/collections/community/crypto/openssl_privatekey_module.html#openssl-privatekey-module
  - name: Generate let's encrypt private key with the default values (4096 bits, RSA)
    become: yes
    openssl_privatekey:
      path: "{{letsencrypt_keys_dir}}/{{ domain_name }}.key"
  
  # https://docs.ansible.com/ansible/latest/collections/community/crypto/openssl_csr_module.html#openssl-csr-module
  - name: Generate an OpenSSL Certificate Signing Request
    become: yes
    community.crypto.openssl_csr:
      path: "{{letsencrypt_csrs_dir}}/{{ domain_name }}.csr"
      privatekey_path: "{{letsencrypt_keys_dir}}/{{ domain_name }}.key"
      common_name: "{{domain_name}}"
  
  # Create letsencrypt challenge.
  - name: Create a challenge for {{domain_name}} using a account key file.
    become: yes
    community.crypto.acme_certificate:
      acme_directory: "{{acme_directory}}"
      acme_version: "{{acme_version}}"
      account_email: "{{acme_email}}"
      terms_agreed: yes
      account_key_src: "{{letsencrypt_account_key}}"
      csr: "{{letsencrypt_csrs_dir}}/{{domain_name}}.csr"
      dest: "{{letsencrypt_certs_dir}}/{{domain_name}}.crt"
      remaining_days: "{{remaining_days}}"
    register: acme_challenge
  
  # Create the directory to hold the validation token.
  - name: "Create .well-known/acme-challenge directory"
    become: yes
    file:
      path: "{{project_path}}/.well-known/acme-challenge"
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=rx,o=rx
  
  # Copy the necessary files for the http-01 challenge.
  - name: "Implement http-01 challenge files"
    become: yes
    copy:
      dest: "{{project_path}}/{{ acme_challenge['challenge_data'][item]['http-01']['resource'] }}"
      content: "{{ acme_challenge['challenge_data'][item]['http-01']['resource_value'] }}"
    with_items:
      - "{{ domain_name }}"
    when: acme_challenge is changed and domain_name|string in acme_challenge['challenge_data']
  
  # Execute letsencrypt challenge.
  - name: Let the challenge be validated and retrieve the cert and intermediate certificate
    become: yes
    community.crypto.acme_certificate:
      account_key_src: "{{letsencrypt_account_key}}"
      csr: "{{letsencrypt_csrs_dir}}/{{domain_name}}.csr"
      cert: "{{letsencrypt_certs_dir}}/{{domain_name}}.crt"
      acme_directory: "{{acme_directory}}"
      acme_version: "{{acme_version}}"
      account_email: "{{acme_email}}"
      challenge: "{{acme_challenge_type}}"
      fullchain: "{{letsencrypt_certs_dir}}/{{domain_name}}-fullchain.crt"
      chain: "{{letsencrypt_certs_dir}}/{{domain_name}}-intermediate.crt"
      remaining_days: "{{remaining_days}}"
      data: "{{ acme_challenge }}"
    when: acme_challenge is changed
  
  ```
  
  The corresponding Nginx config file is:
  
  ```
  upstream {{project_name}} {
      server unix:{{socket_location}};
  }
  
  server {
      listen 80;
      location /.well-known/acme-challenge/ {
          root {{project_path}};
      }
      server_name {{domain_name}} www.{{domain_name}} {{web_server_ip}};
      return 301 https://{{domain_name}}$request_uri;
  }
  
  server {
  
      listen 443 ssl;
      ssl_certificate /etc/letsencrypt/certs/{{domain_name}}-fullchain.crt;
      ssl_certificate_key /etc/letsencrypt/keys/{{domain_name}}.key;
  
      charset utf-8;
      client_max_body_size 4M;
  
      # Serving static files directly from Nginx without passing through uwsgi 
      location /app/static/ {
          alias {{project_path}}/app/static/;
      }
  
      location /.well-known/acme-challenge/ {
          root {{project_path}};
      }
  
      location / {
          # kill cache
          add_header Last-Modified $date_gmt;
          add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
          if_modified_since off;
          expires off;
          etag off;
          uwsgi_pass {{project_name}};
          include {{project_path}}/uwsgi_params;
      }
  }
  
  ```
  
  
  ---
  
  