Terraform Fastly
POC / exploration of the Fastly Terraform provider for managing Fastly CDN services via Infrastructure as Code.
Table of Contents
- Overview
- Project Structure
- Prerequisites
- Quick Start
- Architecture
- Usage
- Variables Reference
- Features
- License
Overview
This project demonstrates how to provision and manage a Fastly CDN service using Terraform and the official fastly/fastly provider (v8+).
It abstracts common CDN configuration patterns — backends, compression (gzip + brotli), HTTP/3, cache control, CORS, IP-based PURGE whitelisting, and domain management — into a reusable Terraform module.
Project Structure
terraform-fastly/
├── modules/
│ └── service-vcl/ # Reusable Fastly service module
│ ├── main.tf # Resource definitions (service, ACL, domain)
│ ├── variables.tf # Module input variables
│ └── provider.tf # Provider requirements (fastly >= 8.0.0)
├── poc/ # Proof-of-Concept / example deployment
│ ├── modules.tf # Module invocation
│ └── variables.tf # Example variable values
├── .gitignore
└── LICENSE
Module: service-vcl
The core module provisions a complete Fastly VCL service with:
| Resource | Purpose |
|---|---|
fastly_service_vcl |
The Fastly service itself — backend, compression, HTTP/3, cache overrides, ACLs, and snippets |
fastly_service_acl_entries |
IP whitelist for secure PURGE requests (conditional — only created when purge_allowed_ips is non-empty) |
fastly_domain |
DNS domain binding for the service |
POC: poc/
A working example that ties everything together with sensible defaults pointing to git.rznet.fr.
Prerequisites
- Terraform ≥ 1.0
- Fastly API Key — obtain from your Fastly account dashboard
- Access to a backend origin server
Quick Start
# 1. Export your Fastly API key
export FASTLY_API_KEY="your_api_key_here"
# 2. Navigate to the POC directory
cd poc
# 3. Initialize, plan, and apply
terraform init
terraform plan
terraform apply
Architecture
┌───────────────────────┐
│ Fastly CDN │
│ │
Domain ──────────►│ ── HTTP/3 enabled │
(var.domain) │ ── gzip + brotli │
│ ── cache control │
│ ── PURGE whitelist │
│ │
└──────┬────────────────┘
│ SSL (var.backend_ssl)
▼
┌──────────────────────┐
│ Backend Origin │
│ (var.backend) │
│ :443 │
└──────────────────────┘
Key flows:
- Request handling — Client hits the Fastly domain → service evaluates VCL → cache lookup → fetch from backend if miss.
- PURGE requests — A dynamic VCL snippet intercepts
PURGErequests and only allows them from IPs in thepurge_ip_whitelistACL. Non-whitelisted IPs receive a403 Forbidden. - Compression — Static assets matching configured extensions and content types are gzip-compressed. Brotli is enabled by default.
Usage
Basic
Simply supply a domain and backend:
module "fastly" {
source = "../modules/service-vcl"
domain = "cdn.example.com"
backend = "origin.example.com"
}
With PURGE Whitelist
module "fastly" {
source = "../modules/service-vcl"
domain = "cdn.example.com"
backend = "origin.example.com"
purge_allowed_ips = ["203.0.113.0/24", "198.51.100.5"]
}
With Full Configuration
module "fastly" {
source = "../modules/service-vcl"
domain = "cdn.example.com"
backend = "origin.example.com"
backend_cert_hostname = "origin.example.com"
backend_port = 443
backend_ssl = true
backend_ssl_check = true
http3 = true
enable_brotli = true
enable_image_optimization = false
default_ttl = 3600
stale_ttl = 86400
cache_override = true
gzip_extensions = ["css", "js", "html", "json"]
gzip_content_types = ["text/html", "text/css", "application/json"]
allowed_cors_domains = ["https://example.com"]
enable_logging = false
enable_waf = false
force_destroy = true
purge_allowed_ips = ["10.0.0.1"]
}
Variables Reference
Module: modules/service-vcl/variables.tf
| Variable | Type | Default | Description |
|---|---|---|---|
domain |
string |
"" |
Fastly domain / service name |
backend |
string |
"" |
Backend origin address |
backend_port |
number |
443 |
Backend port |
backend_ssl |
bool |
true |
Enable SSL to backend |
backend_ssl_check |
bool |
true |
Check backend SSL certificate |
backend_cert_hostname |
string |
"" |
Hostname for SSL certificate validation |
http3 |
bool |
true |
Enable HTTP/3 (QUIC) |
default_ttl |
number |
3600 |
Default cache TTL in seconds |
stale_ttl |
number |
86400 |
Stale-while-revalidate TTL in seconds |
cache_override |
bool |
true |
Enable cache override |
gzip_extensions |
list |
see below | File extensions to gzip compress |
gzip_content_types |
list |
see below | MIME types to gzip compress |
enable_brotli |
bool |
true |
Enable Brotli compression |
enable_image_optimization |
bool |
false |
Enable Fastly image optimization |
enable_logging |
bool |
false |
Enable logging |
log_format |
string |
"%h %l %u %t \"%r\" %>s %b" |
Log format string |
enable_waf |
bool |
false |
Enable Web Application Firewall |
allowed_cors_domains |
list(string) |
[] |
Allowed CORS origins |
purge_allowed_ips |
list(string) |
[] |
IPs/CIDRs allowed to send PURGE requests |
Default gzip extensions: css, js, html, htm, txt, json, xml, woff, woff2
Default gzip content types: text/html, text/plain, text/css, application/javascript, application/json, application/xml, application/x-javascript, text/javascript, text/xml, application/rss+xml, image/svg+xml, application/wasm, font/woff2
Features
| Feature | Status | Notes |
|---|---|---|
| Backend with SSL/TLS | ✅ | Configurable port, cert hostname, SNI |
| Gzip Compression | ✅ | Configurable extensions + content types |
| Brotli Compression | ✅ | Enabled by default |
| HTTP/3 (QUIC) | ✅ | Enabled by default |
| Cache TTL / Stale-While-Revalidate | ✅ | Configurable defaults |
| CORS | ✅ | Whitelist of allowed domains |
| Image Optimization | ✅ | Toggle on/off |
| Domain Management | ✅ | Automatic domain binding |
| PURGE IP Whitelist | ✅ | Dynamic ACL + VCL snippet |
| Logging | ✅ | Configurable format |
| WAF Integration | ✅ | Toggle on/off |
License
This project is licensed under the MIT License. See the LICENSE file for more information.