Skip to content

Create and renew Let's Encrypt certificates using Ansible - and the acme_certificate module

About two years ago I blogged about how to create and renew Let's Encrypt certificates using Ansible. Back then, the "letsencrypt" module was State of the Art. This changed, and with all the Let's Encrypt API changes, the Ansible module changed quite a lot, and is now "acme_certificate". ACME stands for: Automatic Certificate Management Environment, and is the idea that every step along the way of creating and renewing certificates should be automated. No more manual creation of CSR (Certificate Signing Request), sending them per mail or manually uploading them to a CA website, enter your credit card details, and at some point get a mail back with the new signed certificate. All of this (except the credit card - you no longer need one) can be automated, and handled in a matter of seconds.

Time to write an updated blog post for the new module.

 

Besides the obvious change in the module name, a couple other things changed as well. Let's Encrypt bumped their API version to v2, and it is no longer possible to create new certificates for websites using the old v1 API. It is however - for the time being - possible to renew certificates. If you are still use the v1 API: now it's a good time to update to v2. Let's Encrypt is sending out reminders to the email addresses which still use the v1 API.

So, what changed? For starters, I added a step which verifies beforehand if a certificate must be renewed. That's documented here. Using this, I don't need to run through the entire Playbook, and can decide beforehand if a certificate is still OK or needs to be renewed. That saves quite a bit of time.

The "create challenge" step is now using the "acme_certificate" module, and a couple different parameters:

- name: Create challenge
  local_action:
    module: acme_certificate
    account_key_src: "{{ letsencrypt_cert_dir }}/{{ domain }}/account.key"
    csr: "{{ letsencrypt_cert_dir }}/{{ domain }}/domain.csr"
    dest: "{{ letsencrypt_cert_dir }}/{{ domain }}/signed.crt"
    chain_dest: "{{ letsencrypt_cert_dir }}/{{ domain }}/intermediate.pem"
    fullchain_dest: "{{ letsencrypt_cert_dir }}/{{ domain }}/combined.pem"
    account_email: "{{ letsencrypt_account_email }}"
    acme_directory: "{{ letsencrypt_acme_directory }}"
    acme_version: "{{ letsencrypt_acme_version }}"
    challenge: "{{ letsencrypt_challenge }}"
    #agreement: "{{ letsencrypt_agreement }}"
    remaining_days: "{{ letsencrypt_remaining_days }}"
    terms_agreed: yes
  become: no
  run_once: true
  register: create_challenge

The new parameter is: "terms_agreed: yes". The following parameters changed:

  • letsencrypt_acme_directory, from "https://acme-v01.api.letsencrypt.org/directory" to "https://acme-v02.api.letsencrypt.org/directory"
  • letsencrypt_acme_version, from "1" to "2"

The "create certificate" step also changed slightly:

- name: Create certificate
  local_action:
    module: acme_certificate
    account_key_src: "{{ letsencrypt_cert_dir }}/{{ domain }}/account.key"
    csr: "{{ letsencrypt_cert_dir }}/{{ domain }}/domain.csr"
    dest: "{{ letsencrypt_cert_dir }}/{{ domain }}/signed.crt"
    chain_dest: "{{ letsencrypt_cert_dir }}/{{ domain }}/intermediate.pem"
    fullchain_dest: "{{ letsencrypt_cert_dir }}/{{ domain }}/combined.pem"
    account_email: "{{ letsencrypt_account_email }}"
    acme_directory: "{{ letsencrypt_acme_directory }}"
    acme_version: "{{ letsencrypt_acme_version }}"
    challenge: "{{ letsencrypt_challenge }}"
    #agreement: "{{ letsencrypt_agreement }}"
    remaining_days: "{{ letsencrypt_remaining_days }}"
    data: "{{ create_challenge }}"
    terms_agreed: yes
  become: no
  run_once: true
  when: create_challenge is changed
  register: verify_challenge

Here as well the "terms_agreed: yes" parameter is new, otherwise the same variables are used as in the "create challenge" step.

That's it. All the other Playbook code is still the same as in the previous blog post. After about 90 days, all your certificates are on API v2.

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

No comments

Add Comment

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
To leave a comment you must approve it via e-mail, which will be sent to your address after submission.
Form options