Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Latest commit

 

History

History
228 lines (180 loc) · 10.7 KB

File metadata and controls

228 lines (180 loc) · 10.7 KB

1. Define a Fastly service with Terraform

1.0 login/workspace

If using the supplied workstation, login via SSH. The instructor will provide the login username and password.

After authenticated, cd in "workshop" in your $HOME directory.

1.1 main.tf

Open the file main.tf within the terraform directory using your text editor of choice:

What's this? A Terraform file .tf houses your configurations. Configurations are written in HashiCorp Configuration Language (HCL), which is designed for human consumption so users can quickly interpret and understand their infrastructure configuration. Configuration can be in a single file or split across multiple files.

1.2 Variables and interpolation

We need to provide Terraform a Fastly API token so that it can authenticate with the API when creating our services and configuration. There are many ways to pass the token to Terraform, but we will use something called a "vars file". Terraform reads a special file on disk named terraform.tfvars to retrieve our credentials. Don't worry - we won't check these into source control - they are ignored by the .gitignore file in the repository.

You can define a variable in HCL using the syntax below. Add this variable to your main.tf file:

variable "fastly_api_token" {
  type = "string"
}

What's this? Variables allow us to define the parameterization of Terraform configurations. They can have defaults, be provided with a variables file, asked for at execution, or overridden via the CLI. Values can be strings or maps and must be defined before used. Variables help make Terraform flexible, sharable, and extensible.

To then use the value of a variable later within your configuration file you can use the interpolation syntax. Variable values can be interpolated using the ${var.<name>} syntax. Such as:

    api_key = "${var.fastly_api_token}"

When Terraform compiles your configuration the variable will be replaced with the actual value of your api token. This allows us to abstract environment specific state away from the declaration.

1.2.2 .tfvars

Variables and their associated values can be collected in files and passed all at once using the -var-file=foo.tfvars flag.

If a file named terraform.tfvars is present in the current directory, Terraform automatically loads it to populate variables. Your workshop server should already have a .tfvars file in the repository directory. Open it open have a look.

fastly_api_token = "085eac05890ddb4b83b6eebcc9acceb8"

fastly_name = "cat-s83hd"

1.3 Fastly provider

In the same Terraform file main.tf create a Fastly provider definition. We will set the value of the required api_key property to be the fastly_api_token variable we defined previously using the interpolation syntax. Later we will see how can pass api token provided to you at the beginning of the workshop as an environment variable via the CLI.

variable "fastly_api_token" {
  type = "string"
}

provider "fastly" {
  api_key = "${var.fastly_api_token}"
}

What's this? A provider is an abstraction of the API/service provider such as GCP or Fastly. Providers typically require some sort of configuration data such as an API key or credential file.

1.4 Fastly resource

Define a new Fastly service resource block and set the backend to point to the GCS bucket which contains our demo website.

resource "fastly_service_v1" "service" {
  name = "${var.fastly_name}"

  force_destroy = true

  ...

For the purpose of the workshop we've already assigned you a unique service name inside your .tfvars file, this is to ensure it doesn't collide with anyone else in the workshop.

In the same resource block configure your default domain to be used on your service

  # Ficitious domain name
  domain {
    name    = "${var.fastly_name}.fastly-altitude-2017.com"
    comment = "Altitude 2017 workshop domain"
  }

Note: For the purpose of the workshop we've used a fake domain name fastly-altitude-2017.com as the Fastly API requires you to supply a domain when creating a service.

Then define your origin backend; for the purpose of the demo are going to connect to a pre-existing GCS bucket altitude-nyc-abcd-2017-stage which hosts the files for our demo website. However if you were defining a real service, this should point to your origins hostname.

  # GCS backend containing our website files
  backend {
    address               = "storage.googleapis.com"
    ssl_hostname          = "altitude-nyc-abcd-2017-stage.storage.googleapis.com"
    name                  = "altitude-nyc-abcd-2017-stage"
    port                  = 443
    first_byte_timeout    = 3000
    max_conn              = 200
    between_bytes_timeout = 1000
  }

Note: As you can see, we are able to define all of the additional properties for a backend such as max_conn and port, a list of all possible options can be found on the resource documentation.

Finally, our backend requires a header override condition. Just as you can do via the Fastly web UI, we can define the condition as a block within our resource declaration. This is a great example of the power of Varnish allowing us to programatically re-write HTTP headers based on certain conditions.

  # Header override condition to
  header {
    name        = "backend-host-override"
    action      = "set"
    type        = "request"
    destination = "http.Host"
    source      = "\"altitude-nyc-abcd-2017-stage.storage.googleapis.com\""
  }
}

For convenience we can get Terraform to output a link to our newly created service domain once it's successfully created. We do this using an output stanza. Note that we're reusing our fastly_name variable using the interpolation syntax.

output "address" {
  value = "${var.fastly_name}.fastly-altitude-2017.com.global.prod.fastly.net/index.html"
}

Your main.tf should now look something like this:

variable "fastly_api_token" {
  type = "string"
}

variable "fastly_name" {
  type = "string"
}

provider "fastly" {
  api_key = "${var.fastly_api_token}"
}

resource "fastly_service_v1" "service" {
  name = "${var.fastly_name}"

  force_destroy = true

  domain {
    name    = "${var.fastly_name}.fastly-altitude-2017.com"
    comment = "Altitude 2017 workshop domain"
  }

  backend {
    address               = "storage.googleapis.com"
    ssl_hostname          = "altitude-nyc-abcd-2017-stage.storage.googleapis.com"
    name                  = "altitude-nyc-abcd-2017-stage"
    port                  = 443
    first_byte_timeout    = 3000
    max_conn              = 200
    between_bytes_timeout = 1000
  }

  header {
    name        = "backend-host-override"
    action      = "set"
    type        = "request"
    destination = "http.Host"
    source      = "\"altitude-nyc-abcd-2017-stage.storage.googleapis.com\""
  }

  output "address" {
    value = "${var.fastly_name}.fastly-altitude-2017.com.global.prod.fastly.net/index.html"
  }
}

1.5 Terraform CLI

Terraform is a local tool (runs on the current machine rather than the cloud) and all interactions with Terraform occur via the CLI. Using the command terraform, this binary should have been installed at the install stage of the workshop.

Run terraform -h to generate the full list of Terraform commands.

$ terraform -h

For the purpose of the workshop we will be focusing on the commands plan and apply to create our Fastly service.

terraform help

1.6 Terraform plan

Now that we've defined our service configuration in our terraform/main.tf file we want to observe what will happen if we were to deploy and apply these changes to our Fastly account. We can do this by running the terraform plan CLI command:

$ terraform plan ./terraform

What's this? The plan shows you what will happen. You can save plans to guarantee what will happen Plans show reasons for certain actions (such as re-create). Prior to Terraform, users had to blindly guess change ordering, parallelization, and rollout effect.

If you see an error when you first run the command, its most likely a syntax error, fix those then try again. Eventually you should see the change graph output in your terminal.

terraform plan

1.7 Terraform apply

We should now have confidence after viewing our plan that Terraform is going to create our new Fastly service with the desired configuration. Therefore, all that is left is to apply our changes to Fastly in production. As with plan, we can run terraform apply on our main.tf created in the last section. This will create real resources in Fastly.

$ terraform apply ./terraform

What's this? Updates existing resources when updates are allowed otherwise re-creates existing resources when updates are not allowed. Changes are executed in order based on the resource graph and parallelizes changes when possible. Handles and recovers transient errors.

You will be able to watch the resources being created in real-time in our terminal. If successful you should see an output such as:

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

1.7.2 A note about state

You may have noticed Terraform created a terraform.tfstate file when running apply. This is where Terraform persists the state of the last-known arguments and attributes for all resources in our configuration. These contents known as "state" can be stored locally as a JSON file (local state) or stored in a remote shared location such as a GCS bucket (remote state).

Whilst out of scope of this workshop, state is a very powerful feature of Terraform that allows configuration to easily be shared across machines, team members and continuous integration and deployment environment.

1.8 Celebrate! 🎉

If your configuration was successfully applied you should be able to curl your newly created service:

$ curl -v http://<YOUR SERVICE DOMAIN>.global.prod.fastly.net/index.html

HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Content-Type: text/html
Content-Length: 11651
Connection: keep-alive
X-Served-By: cache-lcy1136-LCY
X-Cache: HIT
X-Cache-Hits: 1

If you don't believe this you can even login to the Fastly web interface and look at your configuration.