Terraform and Fastly /
Deploy a custom vcl with Fastly

18/04/2024

Terraform is a powerful tool that helps you build and manage your digital infrastructure, kind of like how you use Lego bricks to build things. Instead of manually setting up servers, databases, and other resources, Terraform lets you write code to define what you want your infrastructure to look like. Let's see how Terraform can drive Fastly

How to install Terraform

On a Mac :

brew install hashicorp/tap/terraform

Verify Terraform is set up :

terraform -help

Create a the main terraform file

Let’s create our terraform project in his own folder :

mkdir terraform-tutorial && cd terraform-tutorial

Then let’s create our main.tf

touch main.tf

Add Fastly as a provider :

terraform {
  required_providers {
    fastly = {
      source  = "fastly/fastly"
      version = ">= 5.7.3"
    }
  }
}

Add a resource :

In the following code you can personalize the demo_tutorialpart the name and the domain name.

domain

For the domain name you can pick what ever you want if you want Fastly to create a temp domain name for you, but you have to follow the format : <name>.global.ssl.fastly.net

force_destroy = true

In Terraform, the force_destroy argument is used in conjunction with certain resources, typically those that manage persistent storage or long-lived infrastructure components. When force_destroy is set to true, it instructs Terraform to destroy the resource even if it has non-Terraform-managed changes or it’s not empty.

For example, if you have an AWS S3 bucket managed by Terraform and you want to delete it, but it still contains objects, Terraform will refuse to delete it by default because it wants to prevent accidental data loss. However, if you set force_destroy to true, Terraform will proceed with the deletion, permanently removing all data from the bucket and then destroying the bucket itself.

The backend block is for the address of your origin :

resource "fastly_service_vcl" "demo_tutorial" {
  name = "Super tutorial"

  domain {
    name    = "antoine-super-tutorial.global.ssl.fastly.net"
  }

  backend {
    address = "httpbin.org"
    name    = "Httbin dummy server"
  }


  force_destroy = true


}

Add an output

In Terraform, the output block is used to define values that should be exposed to the user after Terraform has executed its operations. These values can be useful for displaying important information or passing data between Terraform configurations.

output "active" {
  value = fastly_service_vcl.demo_tutorial.active_version
}

Add the Fastly API Key

provider "fastly" {
  api_key = "NyVYPuAb2Jb3nu_tsQblrMtmk-gw-oBd"
}

The complete main.tf

terraform {
  required_providers {
    fastly = {
      source  = "fastly/fastly"
      version = ">= 5.7.3"
    }
  }
}


# Configure the Fastly Provider
provider "fastly" {
  api_key = "NyVYPuAb2Jb3nu_tsQblrMtmk-gw-oBd"
}



resource "fastly_service_vcl" "demo_tutorial" {
  name = "Super tutorial"

  domain {
    name    = "antoine-super-tutorial.global.ssl.fastly.net"
  }

  backend {
    address = "httpbin.org"
    name    = "Httbin dummy server"
  }



  force_destroy = true


}

output "active" {
  value = fastly_service_vcl.demo_tutorial.active_version
}

Deploy the project on Fastly

terraform init

Terraform relies on plugins called providers to interact with various cloud platforms, APIs, and services. When you run terraform init, it downloads the necessary provider plugins specified in your configuration files and installs them locally. This ensures that Terraform has the required tools to manage your infrastructure.

Run :

terraform init 

tarraform plan

The terraform plan command is used in Terraform to create an execution plan. It essentially simulates the execution of the configuration and displays what actions Terraform will take to achieve the desired state defined in the configuration files

terraform plan

tarraform apply

terraform apply is a command used in Terraform to apply the changes specified in your Terraform configuration to your infrastructure.Terraform executes the actions outlined in the execution plan. This may involve creating new resources, updating existing ones, or destroying resources that are no longer needed.

terraform apply

Add a custom VCL

In the resource fastly_service_vcl we can specify a vcl block to add a custom vcl as specified in the doc

    vcl {
        name    = "my_custom_main_vcl"
        content = file("${path.module}/vcl/main.vcl")
        main    = true
    }

name : the name of your VCL
content : The path to your vcl code. In my case my vcl is stored in a vcl folder and in a main.vcl file.
main : is it the main vcl ?

The complete resource block :

resource "fastly_service_vcl" "test_service" {
    name = "An Example Service"

    domain {
    name = "hello-antoine.global.ssl.fastly.net"
    }

    backend {
    address = "httpbin.org"
    name= "My Test Backend"
    }

    vcl {
        name    = "my_custom_main_vcl"
        content = file("${path.module}/vcl/main.vcl")
        main    = true
    }

    force_destroy = true

}

The custom vcl

the vcl_recv subroutine. This subroutine is executed whenever Varnish receives an incoming HTTP request.

  if (req.url.path == "/anything/here") {
    set req.http.X-CustomHeader = "example";
  }

This if block checks if the request URL path is exactly /anything/here. If it is, then it sets a custom HTTP header named « X-CustomHeader » with the value « example » in the request. This header modification allows for customization of the request handling based on the requested URL.

if (req.url.path == "/anything/not/found") {
    error 600;
}

This if block checks if the request URL path is exactly « /anything/not/found ». If it is, then it generates a synthetic error with status code 600

sub vcl_error { }

This VCL (Varnish Configuration Language) code snippet defines a subroutine vcl_error, which is executed when an error occurs during the processing of a request


if (obj.status == 600) { }

This block of code generates a synthetic response body using the synthetic keyword. It returns a simple HTML document containing an UTF-8 charset meta tag and a « Not Found » message in an <h1> element. This synthetic response is sent to the client when the error condition specified in the if block is met.

Our complete VCL

// vcl/main.vcl

   sub vcl_recv {
    #FASTLY recv

        if (req.url.path == "/anything/here") {
            set req.http.X-CustomHeader = "example";
        }

        if (req.url.path == "/anything/not/found") {
            error 600;
        }

        return(lookup);
    }

    sub vcl_error {
        #FASTLY error
        if (obj.status == 600) {

            set obj.status = 404;
            set obj.http.Content-Type = "text/html";
            synthetic {"
                <html>
                    <head>
                        <meta charset="UTF-8">
                    </head>

                    <body>
                            <h1>Not Found 🤷‍♂️ </h1>
                    </body>
                </html>
            "};

            return(deliver);
        }
}

To deploy as said before :

terraform plan

And then

terraform apply