Today we're excited to announce that Cloudflare Workers are now supported in the Cloudflare Terraform Provider.
Terraform is a fantastic tool for configuring your infrastructure. Traditionally if you wanted to spin up, tear down or update some of your infrastructure you would have to click around on a website or make some API calls, which is prone to human error. With Terraform, you define your infrastructure in simple, declarative configuration files and let Terraform figure out how to make the API calls for you. This also lets you treat your infrastructure like your code. You can check your Terraform configuration files into version control and integrate them into your normal software development workflow.
Terraform integrates with many infrastructure providers including Cloudflare. If you'd like to read more about setting up Terraform with Cloudflare, check out Getting started with Terraform and Cloudflare. In this post, I'm going to focus specifically on how to integrate Cloudflare Workers with Terraform.
In this example we're going to create partyparrot.business, and we're going to serve the whole site out of a worker without any origin server. We're starting from scratch here, but if you're already using Cloudflare workers and want to migrate to managing your workers with Terraform, you'll need to import your existing script and routes so that Terraform knows about them. See the "Importing your existing workers" section at the end.
Prerequisites
Provide your Cloudflare credentials via environment variables
Set
CLOUDFLARE_EMAIL
to your email addressSet
CLOUDFLARE_TOKEN
to your Cloudflare API keyIf you're on an Enterprise plan and want to use multiple scripts, you'll also need to set
CLOUDFLARE_ORG_ID
to your account ID. You can find your account ID by using the List Accounts API
Create the Terraform config file
Create a file with any name and give it a .tf
file extension. This is where we'll define our Terraform resources. In this file, first we'll need to setup the Cloudflare provider:
provider "cloudflare" {}
You could define your credentials in this file, but in general it's better to use environment variables so that you can check the configuration file into version control without including any private data.
Next we're going to create a variable named zone
. One of the benefits about defining the zone in a variable as opposed to hard-coding it is that you can setup a separate staging domain and use the same Terraform configuration as your production domain. See the documentation for more information on working with variables.
variable "zone" {
default = "partyparrot.business"
}
Setting up the worker script
Now let's write our worker script. If you're looking for inspiration, check out some Worker recipes. For this example, I'll use this script and name it party_parrot_worker.js
.
Next we need to add a cloudflare_worker_script
resource to our Terraform config and reference the script file. Open your .tf
file and add the following:
resource "cloudflare_worker_script" "main_script" {
zone = "${var.zone}"
content = "${file("party_parrot_worker.js")}"
}
If you're new to Terraform, check out the Terraform Resource documentation to learn more about this schema. Here we provide 2 parameters, the zone
which references the variable we defined earlier and content
which references the file we just created.
NOTE: The Cloudflare Enterprise plan supports using multiple (named) scripts. To use this, the parameters will be slightly different. Remove the zone
parameter since named scripts are not tied to a particular zone and instead add a name
parameter to define the name of the script. See the cloudflare_worker_script documentation for an example.
Setting up the worker routes
In order for the worker to start handling traffic, we'll also need to define at least one route. To do so, add a cloudflare_worker_route
resource to the Terraform config.
resource "cloudflare_worker_route" "catch_all_route" {
zone = "${var.zone}"
pattern = "*${var.zone}/*"
enabled = true
depends_on = ["cloudflare_worker_script.main_script"]
}
Just like with the script resource, the zone
parameter references the variable we defined earlier.
The pattern
parameter defines which requests should be sent to the worker. In this example we use a route pattern like *partyparrot.business*
which will match all traffic. If, however, you only want your worker to handle a subset of requests to your zone, you can define a more specific pattern like mysubdomain.example.com/*
or *example.com/mypath*
. More information on route patterns is available here.
The enabled
parameter specifies that requests that match the pattern should run the worker. Alternatively, you can set enabled
to false
which would mean that any requests that match the pattern should not run the worker. You can create multiple route patterns, and more-specific route patterns apply before less-specific route patterns. For example, you could create one route pattern like example.com/assets/*
and set enabled = false
then create another pattern like *example.com*
and set enabled = true
. This would enable the worker for all traffic except for requests that match example.com/assets/*
.
Finally, we set depends_on
to point to the script resource we created above. In general, Terraform will try to create resources in parallel, but you may get an error if you try to create a route before you have a script. By using the depends_on
parameter, Terraform will know to create the script first before creating the route.
NOTE: As with the script resource, some of the parameters are different if you're on the Enterprise plan and using multiple scripts. Remove the enabled
parameter and instead set script_name = "${cloudflare_worker_script.your_script_resource.name}"
to specify which script the route should run. By directly referencing the script resource using this syntax, Terraform already knows that the route depends on the script, so you can also remove the depends_on
parameter. You can see more details in the cloudflare_worker_route documentation.
Applying the Terraform config
Now that we've defined our script and route resources in the config file, we're ready to deploy! To initialize Terraform, run terraform init
$ terraform init
Initializing provider plugins...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Now to deploy the changes, run terraform apply
. Terraform will show you a preview of the changes it will make.
$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
+ cloudflare_worker_route.catch_all_route
id: <computed>
enabled: "true"
multi_script: <computed>
pattern: "*partyparrot.business/*"
zone: "partyparrot.business"
zone_id: <computed>
+ cloudflare_worker_script.main_script
id: <computed>
content: "...omitted for brevity..."
zone: "partyparrot.business"
zone_id: <computed>
Plan: 2 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
If everything looks good, type yes
and press return to apply the changes.
cloudflare_worker_script.main_script: Creating...
content: "" => "...omitted for brevity..."
zone: "" => "partyparrot.business"
zone_id: "" => "<computed>"
cloudflare_worker_script.main_script: Creation complete after 1s (ID: zone:partyparrot.business)
cloudflare_worker_route.catch_all_route: Creating...
enabled: "" => "true"
multi_script: "" => "<computed>"
pattern: "" => "*partyparrot.business/*"
zone: "" => "partyparrot.business"
zone_id: "" => "<computed>"
cloudflare_worker_route.catch_all_route: Creation complete after 0s (ID: af595c1bb7cd4d1698c4d6cbcb364662)
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Congratulations, your worker script and route are deployed! You can see the example script running at partyparrot.business.
As you make changes to your script or Terraform config, you can run terraform apply
again and Terraform will figure out what's changed and deploy any updates.
Importing your existing workers
If you're already using Cloudflare Workers but want to start managing them via Terraform, you'll need to let Terraform know about your existing configuration so it knows how to apply changes going forward.
First you’ll need to create your .tf
file and add cloudflare_worker_script
and cloudflare_worker_route
resources for all of your existing scripts and routes.
Next you'll need to individually run the appropriate terraform import ...
command for each script and route resource. The import command takes two arguments:
the identifier of the resource that you defined in your
.tf
file (ex:cloudflare_worker_script.main_script
orcloudflare_worker_route.catch_all_route
)an ID that's used to lookup the resource from the cloudflare API. See the cloudflare_worker_script and cloudflare_worker_route documentation for more information.
Wrapping up
The complete script and terraform configuration file for this example are hosted on Github.
Whether you're already using Cloudflare Workers or just getting started, Terraform can be a great way to manage your Workers configuration. If you're interested in learning more, here's a few useful links: