Table of Contents

Задача

Есть пара зон в Amazon Route53 и несколько сертификатов (в том числе и wild-card) в Amazon Certificate Manager.
С недавних пор Amazon перестал выпускать (и обновлять) сертификаты для зоны .ru.
Необходимо:

Решение

Я нашел проект на GitHub, представляющий собой модуль для terraform, который создает Lambda-функцию со свей необходимой обвязкой (политики, таймер для запуска).
Однако - он не очень-то заработал со свежим terraform, а также выполнял только сохранение сертов в S3.
Я переделал его под свои нужды - https://github.com/KindDevOps/terraform-aws-certbot-lambda/tree/acm_only (ветка acm_only).
В директории examples есть пример рабочего кода.
Данный код получает на входе:

Предполагается, что в AWS Route53 уже созданы зоны с этими доменами.
В итоге код делает вот что:

Для выпуска серта - достаточно выполнить:

terraform init
terraform plan
terraform apply

И затем - запустить созданную Lambda-функцию.

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
    }
  }
}

provider "aws" {
  region = "${var.aws_region}"
}

module "certbot-lambda" {
  source  = "github.com/KindDevOps/terraform-aws-certbot-lambda?ref=acm_only"

  name                                  = "${local.lambda-name}"
  contact_email                         = "${var.certbot_contact_email}"
  certificate_domains                   = "${local.certificate_domains}"
  # Route53 Zones list to apply policy
  hosted_zones_ids                      = data.aws_route53_zone.zones
  certificate_arn                       = aws_acm_certificate.cert.arn
  
  function_trigger_schedule_expression  = "cron(12 20 * * ? *)"
}

locals {
  lambda-name = replace(regex("\\.*(.*)", "${var.certificate_domains[0]}")[0], ".", "-")
  certificate_domains = join(",", [ for domain in var.certificate_domains : regex("\\.*(.*)", domain)[0] ] )
}

data "aws_route53_zone" "zones" {
  for_each = toset( var.certificate_domains )

  name = regex(".*?[[:punct:]]*(.*)", each.value)[0] 
  private_zone = false
}

resource "tls_private_key" "stub" {
  algorithm = "RSA"
}

resource "tls_self_signed_cert" "stub" {
  private_key_pem = tls_private_key.stub.private_key_pem

  subject {
    common_name  = "${var.certificate_domains[0]}"
    organization = "ACME Examples, Inc"
  }

  validity_period_hours = 12

  allowed_uses = [
    "key_encipherment",
    "digital_signature",
    "server_auth",
  ]
}

resource "aws_acm_certificate" "cert" {
  private_key      = tls_private_key.stub.private_key_pem
  certificate_body = tls_self_signed_cert.stub.cert_pem
}

variables.tf

variable "aws_region" {
  type    = string
  default = "us-east-2"
}

variable "certificate_domains" {
  type = list
  default = [
    ".voximplant.com",
    "*.voximplant.com",
    ".voximplant.ru",
    "*.voximplant.ru"
  ]
  # Front dots should be used to allow automatic Route53 zone id detection for second level domains.
}

variable "certbot_contact_email" {
  type    = string
  default = "usik@voximplant.com"
}