Manage your Deno Deploy and Deno Subhosting projects with Terraform
We recently launched Deno Subhosting (built on the Deno Deploy infrastructure), the easiest and most secure way to run and host your usersβ code. Companies can use Subhosting to extend their productsβ functionalities via a developer platform or a third-party application marketplace. Deno Subhostingβs simple API make it easy to integrate into your platform, so you can programmatically manage deployments, projects, and more.
However, many developers prefer to manage business-critical infrastructure in the cloud with infrastructure-as-code patterns such as Terraform, since itβs a safe, reliable, and predictable way to provision and manage cloud services. Now, weβre excited to announce that you are able to use Deno Subhosting and Deno Deploy through Terraform with our official Deno Terraform Provider.
This provider will allow you to manage a large number of projects, custom domains, deployments, and more, using Terraform. We will maintain this provider and keep it in sync with the new Subhosting API.
Though weβre still working to complete the documentation, you can find the auto-generated provider docs here, as well as a number of working examples in the GitHub.
Keep on reading to learn more about:
- Publish a local directory of code to Deno Deploy
- Defining the Terraform file
- Running Terraform to deploy
- Whatβs next
How to publish a local directory of code to Deno Deploy
To give you an example of how the Deno Terraform Provider works, letβs walk through an example that will publish a local directory of code to Deno Deploy.
Given the below project structure (simplified for this example):
.
βββ deno.json
βββ src
βΒ Β βββ main.ts
βββ static
βΒ Β βββ cat.png
βββ terraform
βββ main.tf
Weβd like to deploy src/main.ts
, which accesses static/cat.png
and an
environment variable FOO
:
Deno.serve(async (req) => {
const url = new URL(req.url);
// Respond with `cat.png` if requested to `/cat.png`.
// Otherwise respond with the value of env var `FOO`.
switch (url.pathname) {
case "/cat.png": {
const file = await Deno.open("./static/cat.png");
return new Response(file.readable, {
headers: {
"content-type": "image/png",
},
});
}
default: {
return new Response(`FOO is ${Deno.env.get("FOO")}`);
}
}
});
Defining the Terraform file
Next, letβs define the .tf
file. Note that there are two required parameters:
your organization ID, and an access token which you can generate on the deploy
dashboard. In the example below we hard code these in the terraform file;
alternatively you can pass them via environment variables,
DENO_DEPLOY_ORGANIZATION_ID
and DENO_DEPLOY_TOKEN
, respectively.
terraform {
required_providers {
deno = {
source = "denoland/deno"
}
}
}
provider "deno" {
// As an alternative to specifying your organization ID in the terraform code
// itself, you can also use the `DENO_DEPLOY_ORGANIZATION_ID` environment
// variable.
organization_id = "e1ae3bcc-923e-4930-a6a7-5841e69849ed"
// Similarly, you may omit the token here and use the `DENO_DEPLOY_TOKEN`
// environment variable.
token = "ddo_3JfdN41GxH0PufmzcoahcJtQXcbde144Z7l9"
}
Next, letβs create a project using the deno_project
resource. Note that name
is optional (if itβs omitted, a random name is automatically generated) but it
must be globally unique.
resource "deno_project" "sample_project" {
name = "awesome-deno-40"
}
Then, letβs define βassetsβ using the deno_assets
data source. You can think
of this as a set of files to be uploaded. The first required parameter, path
,
serves as a root directory path where Terraform looks for assets. The second
parameter, pattern
, is a glob string whose matches determine which assets to
be included.
In this example, src/main.ts
and static/cat.png
will be included:
data "deno_assets" "sample_assets" {
path = "../"
pattern = "{src,static}/**/*.{ts,png}"
}
Weβre now ready to create the deployment using the deno_deployment
resource.
This resource accepts parameters project_id
and assets
data that have been
created so far. Other parameters, such as env_vars
can also be provided. (For
more details,
check out this example usage.)
resource "deno_deployment" "sample_deployment" {
project_id = deno_project.sample_project.id
entry_point_url = "src/main.ts"
assets = data.deno_assets.sample_assets.output
env_vars = {
FOO = "42"
}
}
And thatβs it!
Running Terraform
Letβs run terraform plan
to see if we have configured things correctly:
$ cd terraform
$ terraform plan
data.deno_assets.sample_assets: Reading...
data.deno_assets.sample_assets: Read complete after 0s
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# deno_deployment.sample_deployment will be created
+ resource "deno_deployment" "sample_deployment" {
+ assets = {
+ "src/main.ts" = {
+ content_source_path = "../src/main.ts"
+ kind = "file"
},
+ "static/cat.png" = {
+ content_source_path = "../static/cat.png"
+ kind = "file"
},
}
+ created_at = (known after apply)
+ deployment_id = (known after apply)
+ domains = (known after apply)
+ entry_point_url = "src/main.ts"
+ env_vars = {
+ "FOO" = "42"
}
+ project_id = (known after apply)
+ status = (known after apply)
+ updated_at = (known after apply)
+ uploaded_assets = (known after apply)
}
# deno_project.sample_project will be created
+ resource "deno_project" "sample_project" {
+ created_at = (known after apply)
+ id = (known after apply)
+ name = "awesome-deno-40"
+ updated_at = (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
Looks good! Now letβs run terraform apply
.
$ terraform apply
data.deno_assets.sample_assets: Reading...
data.deno_assets.sample_assets: Read complete after 0s
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# deno_deployment.sample_deployment will be created
+ resource "deno_deployment" "sample_deployment" {
+ assets = {
+ "src/main.ts" = {
+ content_source_path = "../src/main.ts"
+ kind = "file"
},
+ "static/cat.png" = {
+ content_source_path = "../static/cat.png"
+ kind = "file"
},
}
+ created_at = (known after apply)
+ deployment_id = (known after apply)
+ domains = (known after apply)
+ entry_point_url = "src/main.ts"
+ env_vars = {
+ "FOO" = "42"
}
+ project_id = (known after apply)
+ status = (known after apply)
+ updated_at = (known after apply)
+ uploaded_assets = (known after apply)
}
# deno_project.sample_project will be created
+ resource "deno_project" "sample_project" {
+ created_at = (known after apply)
+ id = (known after apply)
+ name = "awesome-deno-40"
+ updated_at = (known after apply)
}
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: yes
deno_project.sample_project: Creating...
deno_project.sample_project: Creation complete after 0s [id=82672716-5aee-4279-a7da-3a3554ec9f74]
deno_deployment.sample_deployment: Creating...
deno_deployment.sample_deployment: Creation complete after 4s
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Done! Letβs take a look at the Terraform state to get a URL to access the deployment:
$ terraform state show deno_deployment.sample_deployment
# deno_deployment.sample_deployment:
resource "deno_deployment" "sample_deployment" {
// ...
deployment_id = "rpxayrskrhzt"
domains = [
"awesome-deno-40-rpxayrskrhzt.deno.dev",
]
entry_point_url = "src/main.ts"
// ...
}
To check if our code is successfully deployed on Deno Deploy and works properly,
letβs visit
https://awesome-deno-40-rpxayrskrhzt.deno.dev/
, which should show the configured environment variable. Also, going to
https://awesome-deno-40-rpxayrskrhzt.deno.dev/cat.png
should show your cute cat picture.
Whatβs next
Even though Deno Subhosting provides simple APIs to manage projects, deployments, and more on Deno Deploy, for those familiar with infrastructure-as-code patterns, using the Deno Terraform Provider can be simpler, especially when coordinating hundreds of projects.
We hope to add documentation and more examples, but if you would like technical help, please join us in our active Discord or create an issue here.
π‘οΈ Learn how you can unlock the last 10% of your platformβs value with Deno Subhosting in our livestream where Kevin and Bert offer Subhosting examples from innovative companies and demo the Subhosting API.