# Help with acme\_certificate / letsencrypt and empty challenge\_data

**URL:** <https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214>\
**Category:** Ansible Project\
**Tags:** ubuntu\
**Created:** [January 18, 2022, 7:04am UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214 "2022-01-18T07:04:57Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jason\_Wood](https://avatars.discourse-cdn.com/v4/letter/j/3bc359/32.png) [@Jason\_Wood](https://forum.ansible.com/u/Jason_Wood)\
**Post date:** [January 18, 2022, 7:04am UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/1 "2022-01-18T07:04:57Z")

</div>

I’m fairly new to this. I’m using ansible 2.9.6 on Ubuntu 20.04.

I followed the [Digital Ocean tutorial](https://www.digitalocean.com/community/tutorials/how-to-acquire-a-let-s-encrypt-certificate-using-ansible-on-ubuntu-18-04) to get started with letsencrypt/acme\_certificate

The problem I run into is that if I run the play again, I get an error when I try to host the challenge data because challenge\_data is an empty object.

I see in the official docs there is a “when” condition in the example:

when: sample\_com\_challenge is changed and ‘[sample.com](http://sample.com)’ in sample\_com\_challenge[‘challenge\_data’]

…but I am unsure how to write this statement when the domain name comes from a variable. I tried both of the following:

when: acme\_challenge\_mydomain is changed and inventory\_hostname in acme\_challenge\_mydomain[‘challenge\_data’]  
and  
when: acme\_challenge\_mydomain is changed and ‘{{ inventory\_hostname }}’ in acme\_challenge\_mydomain[‘challenge\_data’]

but in both cases the task executes even though I can see with a debug task that challenge\_data is empty:  
…“challenge\_data”: {},…

My second question is… I have remaining\_days set to 91, and there are 89 days left, so shouldn’t I have data in challenge\_data? The docs for remaining\_days states “If cert\_days \< remaining\_days, then it will be renewed. If the certificate is not renewed, module return values will not include challenge\_data.”

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [January 18, 2022, 5:01pm UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/2 "2022-01-18T17:01:00Z")

</div>

Hi,

> I'm fairly new to this. I'm using ansible 2.9.6 on Ubuntu 20.04.

that version is really old and outdated. You should upgrade at least to  
the latest 2.9.x release (2.9.27), or even to the latest Ansible  
release (5.2.0).

This is the correct syntax (assuming your domain name is  
inventory\_hostname):

> when: acme\_challenge\_mydomain is changed and inventory\_hostname in  
> acme\_challenge\_mydomain['challenge\_data']

Without knowing your playbook...

> but in both cases the task executes even though I can see with a  
> debug task that challenge\_data is empty:  
> ..."challenge\_data": {},...

...it's hard to say why this happens.

Same for this:

> My second question is... I have remaining\_days set to 91, and there  
> are 89 days left, so shouldn't I have data in challenge\_data?

There should be. (You can also set `force: true` to force regeneration.)

Are you sure that you are really running the playbook that you  
modified, and not something else?

Cheers,  
Felix

---

<div class="post-metadata">

**Author:** ![Jason\_Wood](https://avatars.discourse-cdn.com/v4/letter/j/3bc359/32.png) [@Jason\_Wood](https://forum.ansible.com/u/Jason_Wood)\
**Post date:** [January 18, 2022, 5:12pm UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/3 "2022-01-18T17:12:31Z")

</div>

inventory\_hostname is not the domain, it is a variable which contains the domain. That’s the part where I’m not sure how to reference it.

I will try upgrading to see if that changes anything. Thanks!

---

<div class="post-metadata">

**Author:** ![Parth\_Patel](https://avatars.discourse-cdn.com/v4/letter/p/e9a140/32.png) [@Parth\_Patel](https://forum.ansible.com/u/Parth_Patel)\
**Post date:** [January 18, 2022, 5:52pm UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/4 "2022-01-18T17:52:15Z")

</div>

How inventory\_hostname would contain domain ?

Its just a special variable pre defined in ansible which contains name of the host for which task or play runs

---

<div class="post-metadata">

**Author:** ![Jason\_Wood](https://avatars.discourse-cdn.com/v4/letter/j/3bc359/32.png) [@Jason\_Wood](https://forum.ansible.com/u/Jason_Wood)\
**Post date:** [January 18, 2022, 6:00pm UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/5 "2022-01-18T18:00:56Z")

</div>

I added these debugs:

- name: Debug when statement  
debug:  
var: acme\_challenge\_mydomain is changed and inventory\_hostname in acme\_challenge\_mydomain[‘challenge\_data’]

- name: Debug the debug  
debug:  
var: acme\_challenge\_mydomain is changed and inventory\_hostname in acme\_challenge\_mydomain[‘authorizations’]

And these output false and true, as expected

But right after that, I have the exact same statement in when:

- name: “Implement http-01 challenge files”  
copy:  
content: “{{ acme\_challenge\_mydomain[‘challenge\_data’][inventory\_hostname][‘http-01’][‘resource\_value’] }}”  
dest: “/opt/FileMaker/FileMaker Server/HTTPServer/htdocs/{{ acme\_challenge\_mydomain[‘challenge\_data’][inventory\_hostname][‘http-01’][‘resource’] }}”  
owner: root  
group: root  
mode: u=rw,g=r,o=r  
when: **acme\_challenge\_mydomain is changed and inventory\_hostname in acme\_challenge\_mydomain[‘challenge\_data’]**

…but this task is still returning this error:

fatal: [travelweek.ddb.space]: FAILED! =\> {“msg”: “The task includes an option with an undefined variable. The error was: ‘dict object’ has no attribute ‘travelweek.ddb.space’\n\nThe error appears to be in ‘/home/ubuntu/fms-ansible-master/letsencrypt-issue.yml’: line 83, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: "Implement http-01 challenge files"\n ^ here\n”}

Note: I have upgraded ansible and ansible --version now returns ansible [core 2.12.1]

> How inventory\_hostname would contain domain ?  
> Its just a special variable pre defined in ansible which contains name of the host for which task or play runs

That is exactly what I want and it seems to be working as I expect. Notice the true result from my second debug task above. I also know through debug that inventory\_hostname contains the domain I want the certificate issued for. I also debug the acme\_challenge\_mydomain object and I see the domain there in authorizations, which is why the statement returns true.

---

<div class="post-metadata">

**Author:** ![racke](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/racke/32/4767_2.png) [@racke](https://forum.ansible.com/u/racke)\
**Post date:** [January 18, 2022, 6:36pm UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/6 "2022-01-18T18:36:40Z")

</div>

> How inventory\_hostname would contain domain ?

If you put foo.example.com into the inventory, the inventory\_hostname would include the domain example.com.

Regards  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Racke

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [January 18, 2022, 6:56pm UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/7 "2022-01-18T18:56:46Z")

</div>

Hi,

> But right after that, I have the exact same statement in when:
> 
> - name: "Implement http-01 challenge files"  
> copy:  
> content: "{{  
> acme\_challenge\_mydomain['challenge\_data'][inventory\_hostname]['http-01']['resource\_value']  
> }}"  
> dest: "/opt/FileMaker/FileMaker Server/HTTPServer/htdocs/{{  
> acme\_challenge\_mydomain['challenge\_data'][inventory\_hostname]['http-01']['resource']  
> }}"  
> owner: root  
> group: root  
> mode: u=rw,g=r,o=r  
> when: \*acme\_challenge\_mydomain is changed and inventory\_hostname in  
> acme\_challenge\_mydomain['challenge\_data']\*
> 
> ...but this task is still returning this error:

unfortunately in the text version of your email, all indentation is  
gone. But in the HTML view online  
([https://groups.google.com/g/ansible-project/c/cKmSS0VhyYo/m/oDCXtZFLAAAJ](https://groups.google.com/g/ansible-project/c/cKmSS0VhyYo/m/oDCXtZFLAAAJ))  
one can see that `when:` is indented at the wrong level. It is not an  
option to the `copy` module, but belongs on the task level, i.e. the  
same level as `copy:`.

If you remove two spaces before `when:` it should work.

Cheers,  
Felix

---

<div class="post-metadata">

**Author:** ![Jason\_Wood](https://avatars.discourse-cdn.com/v4/letter/j/3bc359/32.png) [@Jason\_Wood](https://forum.ansible.com/u/Jason_Wood)\
**Post date:** [January 18, 2022, 7:44pm UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/8 "2022-01-18T19:44:57Z")

</div>

oh my! Thank you!!!

It appears that the first example in the docs has this error:  
[https://docs.ansible.com/ansible/latest/collections/community/crypto/acme\_certificate\_module.html#acme-certificate-module](https://docs.ansible.com/ansible/latest/collections/community/crypto/acme_certificate_module.html#acme-certificate-module)

So with that solved, I’m left with the problem where challenge\_data is empty even if I specify remaining\_days: 91 . I also tried adding force: yes or force: true

In the object created by acme\_challenge task, I see that cert\_days is 89

It works if I delete the /etc/letsencrypt directory, which includes the account key, certs, csr, etc (so basically we’re starting over from scratch).

I only really care if it works when it gets to under 30 days. This is just for testing.

Here is my play from the top all the way down to the first run of acme\_certificate

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [January 18, 2022, 9:00pm UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/9 "2022-01-18T21:00:08Z")

</div>

Hi,

> It appears that the first example in the docs has this error:  
> [https://docs.ansible.com/ansible/latest/collections/community/crypto/acme\_certificate\_module.html#acme-certificate-module](https://docs.ansible.com/ansible/latest/collections/community/crypto/acme_certificate_module.html#acme-certificate-module)

oh, indeed! Thanks for spotting that! I've created a PR to fix it  
([https://github.com/ansible-collections/community.crypto/pull/382](https://github.com/ansible-collections/community.crypto/pull/382)).

> So with that solved, I'm left with the problem where challenge\_data  
> is empty even if I specify remaining\_days: 91 . I also tried adding  
> force: yes or force: true
> 
> In the object created by acme\_challenge task, I see that cert\_days is  
> 89
> 
> It works if I delete the /etc/letsencrypt directory, which includes  
> the account key, certs, csr, etc (so basically we're starting over  
> from scratch).

That's not how it should be done 🙂

> I only really care if it works when it gets to under 30 days. This is  
> just for testing.
> 
> Here is my play from the top all the way down to the first run of  
> acme\_certificate
> 
> ---  
> - hosts: fms  
> &nbsp;&nbsp;become: true  
> &nbsp;&nbsp;tasks:
> 
> &nbsp;&nbsp;- name: "Create required directories in /etc/letsencrypt"  
> &nbsp;&nbsp;&nbsp;&nbsp;file:  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path: "/etc/letsencrypt/{{ item }}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state: directory  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;owner: root  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;group: root  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode: u=rwx,g=x,o=x  
> &nbsp;&nbsp;&nbsp;&nbsp;with\_items:  
> &nbsp;&nbsp;&nbsp;&nbsp;- account  
> &nbsp;&nbsp;&nbsp;&nbsp;- certs  
> &nbsp;&nbsp;&nbsp;&nbsp;- csrs  
> &nbsp;&nbsp;&nbsp;&nbsp;- keys
> 
> &nbsp;&nbsp;- name: "Generate a Let's Encrypt account key"  
> &nbsp;&nbsp;&nbsp;&nbsp;shell: "if [! -f {{ letsencrypt\_account\_key }}]; then openssl  
> genrsa 4096 | sudo tee {{ letsencrypt\_account\_key }}; fi"

BTW, you can use `creates:` to avoid having to use the `if` construct  
([https://docs.ansible.com/ansible/latest/collections/ansible/builtin/shell\_module.html#parameter-creates](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/shell_module.html#parameter-creates)).

> &nbsp;&nbsp;- name: "Generate Let's Encrypt private key"  
> &nbsp;&nbsp;&nbsp;&nbsp;shell: "openssl genrsa 4096 | sudo tee /etc/letsencrypt/keys/{{  
> inventory\_hostname }}.key"
> 
> &nbsp;&nbsp;- name: "Generate Let's Encrypt CSR"  
> &nbsp;&nbsp;&nbsp;&nbsp;shell: "openssl req -new -sha256 -key /etc/letsencrypt/keys/{{  
> inventory\_hostname }}.key -subj \"/CN={{ inventory\_hostname }}\" |  
> sudo tee /etc/letsencrypt/csrs/{{ inventory\_hostname }}.csr"  
> &nbsp;&nbsp;&nbsp;&nbsp;args:  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;executable: /bin/bash

Also you might be interested in using the openssl\_privatekey module to  
create the private keys, and the openssl\_csr module to create the CSR.

> &nbsp;&nbsp;- name: "Begin Let's Encrypt challenges"  
> &nbsp;&nbsp;&nbsp;&nbsp;acme\_certificate:  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acme\_directory: "{{ acme\_directory }}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acme\_version: "{{ acme\_version }}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;account\_key\_src: "{{ letsencrypt\_account\_key }}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;account\_email: "{{ acme\_email }}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;terms\_agreed: 1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;challenge: "{{ acme\_challenge\_type }}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;csr: "{{ letsencrypt\_csrs\_dir }}/{{ inventory\_hostname }}.csr"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dest: "{{ letsencrypt\_certs\_dir }}/{{ inventory\_hostname }}.crt"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fullchain\_dest: "{{ letsencrypt\_certs\_dir }}/fullchain\_{{ inventory\_hostname }}.crt"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remaining\_days: 91  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;force: yes  
> &nbsp;&nbsp;&nbsp;&nbsp;register: acme\_challenge\_mydomain

This looks correct so far.

I guess afterwards you have the copy task, and then another  
acme\_certificate task. Which `when:` condition are you using for the  
latter? I hope only `when: acme_challenge_mydomain is changed` and not  
the same condition as for the copy task.

(The copy task is not always necessary - Let's Encrypt is caching valid  
authorizations for some days -, but the other acme\_certificate needs to  
be run if you want a certificate.)

Cheers,  
Felix

---

<div class="post-metadata">

**Author:** ![Jason\_Wood](https://avatars.discourse-cdn.com/v4/letter/j/3bc359/32.png) [@Jason\_Wood](https://forum.ansible.com/u/Jason_Wood)\
**Post date:** [January 19, 2022, 12:43am UTC](https://forum.ansible.com/t/help-with-acme-certificate-letsencrypt-and-empty-challenge-data/36214/10 "2022-01-19T00:43:17Z")

</div>

Thanks - I’m understanding much better now.

The presence of challenge data is not necessarily required for certificate renewal, if previous challenge data is still valid, you can just skip the copy step and the second run will install a new certificate.

If no certificate is needed because remaining\_days is lower than cert\_days, the first run will return changed=false and the second run doesn’t need to run.

I will also take your advice and implement creates: and the openssl\_privatekey and openssl\_csr modules!

Thanks for all your help!
