Introduction

Dans mon précédent article, j’avais montré la création avec Terraform d’un compartiment S3 pour accueillir un site statique (généré avec Hugo). Malheureusement, ce site n’était accessible qu’en HTTP et l’URL n’était pas personnalisable.

Pour répondre à ce besoin, l’utilisation de CloudFront, service de content delivery network (CDN), permet de :

  • Fournir plus rapidement le contenu statique

  • Personnaliser le domaine

  • Limiter le coût d’utilisation (le coût d’utilisation étant moindre que celle de S3)

La documentation d’AWS présente bien les étapes nécessaires. Cependant, mon article va se différencier sur les points suivants :

  • Utilisation de Terraform

  • Utilisation d’un seul domaine

  • Pas de conservation des logs d’accès

  • Pas d’utilisation de Route53 pour les enregistrements DNS. Route53 est un service très pratique pour la gestion des enregistrements DNS mais qui comportent des prérequis pour la création d’un domaine. En outre, j’en disposais déjà d’un et j’avais envie de faire autre chose que le demi-million d’article sur les sites statiques avec AWS.

Droits

Il faut de nouveaux droits pour le déploiement, pour pouvoir déployer des nouvelles ressources. En plus de "AmazonS3FullAccess", il faut donc accorder les politiques "CloudFrontFullAccess" et "AWSCertificateManagerFullAccess".

Déploiement multi-région

L’utilisation de CloudFront impose que le certificat soit dans us-east-1. Ce n’est pas le cas de toutes mes ressources. J’ai donc défini un nouveau fournisseur AWS pour la région us-east-1 :

provider.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.3.0"
    }
  }
  required_version = ">= 1.1.6"
}

provider "aws" {
  region = var.aws_region
}

provider "aws" {
  region = "us-east-1"
  alias = "useast1"
}

Il est alors possible d’utiliser cette région spécifiquement, comme pour le certificat :

resource "aws_acm_certificate" "certificate" {
  domain_name       = var.www_domain_name
  validation_method = "DNS"
  provider          = aws.useast1
}

Configuration de Terraform et création des ressources

Le fichier main.tf de mon précédent article a été modifié pour intégrer le certificat et CloudFront :

...
resource "aws_acm_certificate" "certificate" { (1)
  domain_name       = var.www_domain_name
  validation_method = "DNS"
  provider          = aws.useast1
}

resource "aws_acm_certificate_validation" "certificate" { (2)
  certificate_arn = aws_acm_certificate.certificate.arn
  provider        = aws.useast1
}

resource "aws_cloudfront_distribution" "www_distribution" {
  // origin is where CloudFront gets its content from.
  origin { (3)
    custom_origin_config {
      // These are all the defaults.
      http_port              = "80"
      https_port             = "443"
      origin_protocol_policy = "http-only"
      origin_ssl_protocols   = ["TLSv1", "TLSv1.1", "TLSv1.2"]
    }

    // Here we're using our S3 bucket's URL!
    domain_name = aws_s3_bucket.site.website_endpoint
    // This can be any name to identify this origin.
    origin_id = var.www_domain_name
  }

  enabled             = true
  default_root_object = "index.html"

  // All values are defaults from the AWS console.
  default_cache_behavior {
    viewer_protocol_policy = "redirect-to-https"
    compress               = true
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    target_origin_id       = var.www_domain_name // This needs to match the `origin_id` above.
    min_ttl                = 0
    default_ttl            = 86400
    max_ttl                = 31536000

    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
  }
  aliases = [var.www_domain_name] (4)
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  // Here's where our certificate is loaded in!
  viewer_certificate { (5)
    acm_certificate_arn = aws_acm_certificate.certificate.arn
    ssl_support_method  = "sni-only"
  }
}

Les étapes seront alors : 1. Création d’un compartiment identique au précédent article 2. Création d’un certificat dans AWS Certificate Manager (cf. 1) 3. Validation du certificat (cf. 2) 4. Création d’une distribution CloudFront avec pour source/origine le compartiment S3 (cf. 3), le domaine personnalisé (cf. 4) et l’utilisation du certificat précédemment testé (cf. 5)

J’ai fait le choix d’une validation DNS au lieu de mail (cf. Validation de la propriété du domaine).

Il est possible de récupérer le challenge DNS grâce à la sortie :

outputs.tf
output "domain_validation_options" {
  description = "Validation info to get a certificate"
  value       = aws_acm_certificate.certificate.domain_validation_options
}

Comme une validation DNS est nécessaire au milieu de la procédure, le premier terraform apply échoue. Il est alors possible de récupérer le challenge DNS grâce à la commande terraform output. Il faut alors créer l’enregistrement DNS idoine.

Publication

Le contenu du blog peut être regénéré pour utiliser le domaine personnalisé avant d’être publié sur le compartiment S3  :

hugo -b https://mondomaine.com
aws s3 cp public/ s3://monbuckets3/ --recursive

Conclusion

Très simplement, nous avons pu mettre en place un blog statique avec une infrastructure réutilisable facilement grâce à Terraform.

D’autres composants AWS existent mais il sera intéressant de les comparer, notamment au niveau coût, avec cette solution.