Terraform: creazione automatizzata di siti statici con AWS S3!

S3 & Terraform

Vi ricordate il mese scorso quando ho pubblicato l’articolo AWS S3 Hosting Static Web Site dove vi illustravo come creare un sito statico su AWS sfruttando il servizio S3? Ora ho deciso di mostrarvi come ottenere lo stesso risultato, ma utilizzando Terraform scoperto ad inizio dell’anno ( Terraform IaC Infrastructure as Code )⬇️

ATTENZIONE: Lo script viene fornito ‘as-is’ senza nessuna responsabilità per il comportamento. L’utilizzo su una vostra sottoscrizione è totalmente a vostro rischio e pericolo!

Terraform Cookbook - Second Edition: Provision, run, and scale cloud architecture with real-world examples using Terraform

File utilizzati

Prima di mostrare come creare un sito statico su S3 è necessario che vi mostri l’alberatura dei file presenti nel progetto per rendervi più chiari i passaggi successivi

  • files-to-upload: è una cartella contenenti i file che vogliamo caricare sul sito statico una volta creato. In questa cartella possiamo trovare il file index.html e aws-s3-hosting-static-web-site.jpg
  • main: file con estensione .tf con tutto lo script necessario per automatizzare la creazione
  • output: file con estensione .tf con al suo interno la definizione dei dati che vogliamo vedere una volta concluso il tutto.

Come avranno notato i più attenti (o meglio con una base di terraform) ho omesso il file variable nella lista ed annesso terraform.tfvars. Questo per non “complicare” troppo lo script per chi è alle prime armi.

main.tf

All’interno del file in questione troveremo tutto quello che ci occorre per procedere all’automazione del nostro sito statico di prova.

  1. definizione del provider aws
  2. utilizzo di aws_s3_bucket per la creazione del bucket
  3. utilizzo di aws_s3_bucket_policy per assegnare le policy al bucket creato al punto 2
  4. utilizzo di aws_s3_bucket_website_configuration per configurare il bucket in questione per renderlo un sito statico
  5. utilizzo di aws_s3_bucket_acl per dare le ACL pubbliche al bucket
  6. utillizzo di aws_s3_object per copiare i file presenti nella cartella files-to-upload all’interno del sito appena creato.

Come scritto in precedenza, non avendo utilizzato il file delle variabili la sola modifica che dovrete fare per rendere operativo (o meglio personalizzato lo script) è la modifica alla risorsa aws_s3_bucket dove si definisce il nome del bucket sostituendo your-bucket-name-here con quello che desiderate.

Prima di entrare nel vivo dello script vi voglio fare notare la region da me utilizzata. Per i miei esperimenti/ studio utilizzo us-east-1 in modo da separare le risorse del tutto dal contesto europeo. Se volete utilizzare datacenter piu vicino a noi vi consiglio di utilizzare eu-central-1 (Frankfurt) oppure eu-south-1 (Milan). Di base -in casi produttivi- applico la regola di tenere tutte le stesse risorse vicine tra loro e geograficamente vicine agli utilizzatori finali. Tradotto: dire “ho messo il sito statico in us-east-1 (Northern Virginia) in quanto mi pare figo, ma tutti i miei utenti sono italiani” sappiate che non risulta la scelta migliore.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}


# Configure the AWS Provider
provider "aws" {
  region = "us-east-1"
}


resource "aws_s3_bucket" "s3_bucket" {
  bucket = "your-bucket-name-here"
}

resource "aws_s3_bucket_policy" "s3_bucket_policy" {
  bucket = aws_s3_bucket.s3_bucket.id
  policy = jsonencode(
    {
        "Version": "2012-10-17",
        "Statement": [
          {
              "Sid": "PublicReadGetObject",
              "Effect": "Allow",
              "Principal": "*",
              "Action": [
                  "s3:GetObject"
              ],
              "Resource": [
                "arn:aws:s3:::${aws_s3_bucket.s3_bucket.bucket}",
                "arn:aws:s3:::${aws_s3_bucket.s3_bucket.bucket}/*"
              ]
          }
        ]
    }
  )
}

resource "aws_s3_bucket_website_configuration" "s3_bucket_website_configuration" {
  bucket = aws_s3_bucket.s3_bucket.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "error.html"
  }

  routing_rule {
    condition {
      key_prefix_equals = "docs/"
    }
    redirect {
      replace_key_prefix_with = "documents/"
    }
  }
}

resource "aws_s3_bucket_acl" "s3_bucket_acl" {
  bucket = aws_s3_bucket.s3_bucket.id
  acl    = "public-read"
}


resource "aws_s3_object" "s3_object" {
  bucket   = aws_s3_bucket.s3_bucket.bucket
  for_each = fileset("files-to-upload/", "*")
  key      = each.value
  source = "files-to-upload/${each.value}"
  etag   = filemd5("files-to-upload/${each.value}")
  acl    = "public-read"

  content_type = endswith(each.value, "html") ? "text/html" : "image/jpg"
}

Una volta presa coscienza di cosa implica il lancio di questo script sulla vostra sottoscrizione e ricordo essere una vostra responsabilità i comandi da usare sono i seguenti

terraform init

1
2
3
4
5
6
7
Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v4.55.0

Terraform has been successfully initialized!

terraform validate

1
Success! The configuration is valid.

terraform apply

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Plan: 6 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + static_web_site_url = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:

A questo punto -solamente se siete certi di procedere- potete scrivere yes per applicare tutte le modifiche.

output.tf

Il file output conterrà tutto quello che vogliamo mostrare alla fine dell’apply.

1
2
3
output "static_web_site_url" {    
    value = aws_s3_bucket_website_configuration.s3_bucket_website_configuration.website_endpoint
}

Per vedere il tutto possiamo sfruttare due modalità

  • leggerlo direttamente in console
  • aprire il file terraform.tfstate

In questo esempio desidero mostravi il file terraform tfstate ed il suo contenuto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "version": 4,
  "terraform_version": "1.3.7",
  "serial": 65,
  "lineage": "f9e2082f-2933-e92e-8121-ca18855752d6",
  "outputs": {
    "static_web_site_url": {
      "value": "<your-bucket-name-here>.s3-website-us-east-1.amazonaws.com",
      "type": "string"
    }
  },
  "resources": [
    {
    }
  ],
  "check_results": null
}

Tips

  • Se non volete diventare matti per capire come mai il vostro sito non funziona correttamente nonostante i file caricati ricordatevi di assegnare il Content Type corretto. Nel mio esempio avendo solamente un file html ed una jpg vado a capire di quale file si tratta semplicemente con un endswith sul nome del file.

  • Se invece di caricare i file in questo modo preferite farlo da portale web col drag and drop allora il blocco aws_s3_object potete rimuoverlo del tutto.

  • Avete notato che non ho utilizzato depends_on all’interno dello script? Questo perche’ nel momento dell’esecuzione Terraform ottimizza il tutto cercando di parallelizzare il lavoro e riesce in maniera intelligente a capire lui stesso la dipendenza tra le risorse.

Video Blog

Se -dopo avere letto tutta la spiegazione- volete vederla messa in pratica ecco per voi il video tutorial per creare un sito web statico su AWS.

Se invece desideri vedere come crearlo dal portale AWS ecco il video tutorial.