This Terraform project provisions a Cloudflare R2 object storage bucket using the official Cloudflare Terraform provider.
- Provisions a Cloudflare R2 bucket via Infrastructure as Code
- Configurable bucket name, location, jurisdiction, and storage class
- Sensitive API token handling through Terraform variables
- Outputs bucket name and ID for downstream use (e.g., Worker bindings)
- Terraform
>= 1.0 - A Cloudflare account
- A Cloudflare API Token with R2 Edit permissions
- Your Cloudflare Account ID
- Log in to the Cloudflare dashboard.
- Go to My Profile → API Tokens, or open this direct link.
- Click Create Token.
- Under Custom token, click Get started.
- Give the token a descriptive name (e.g.,
terraform-r2). - Under Permissions, add:
Account→R2→Edit
- Under Account Resources, select the account you want this token scoped to (e.g.,
Include→<Your Account>). - (Optional) Set TTL and IP Address Filtering as needed.
- Click Continue to summary → Create Token.
- Copy the generated token immediately and store it securely — it won't be shown again.
Use a scoped API token (R2 Edit only) rather than your Global API Key for least-privilege access.
Option A — From the dashboard URL
- Log in to the Cloudflare dashboard.
- Select any domain/account from your home page.
- Look at the URL — it will be in the form:
https://dash.cloudflare.com/<ACCOUNT_ID>/... - Copy the
<ACCOUNT_ID>segment.
Option B — From the Account home page
- Log in to the Cloudflare dashboard.
- Click Workers & Pages (or any domain overview).
- On the right sidebar, find Account ID and click the copy icon.
Option C — Via the API
curl -X GET "https://api.cloudflare.com/client/v4/accounts" \
-H "Authorization: Bearer <YOUR_API_TOKEN>" \
-H "Content-Type: application/json"The result[].id field in the JSON response is your Account ID.
git clone https://github.com/marcuwynu23/terraform-cloudflare-r2.git
cd terraform-cloudflare-r2
cp terraform.tfvars.example terraform.tfvarscloudflare_api_token = "your-api-token-here"
cloudflare_account_id = "your-account-id-here"
bucket_name = "my-r2-bucket"terraform initterraform planterraform applyterraform destroy| Name | Description | Type | Default | Required |
|---|---|---|---|---|
cloudflare_api_token |
Cloudflare API Token with R2 permissions | string |
n/a | yes |
cloudflare_account_id |
Cloudflare Account ID | string |
n/a | yes |
bucket_name |
Name of the R2 bucket (3-64 chars) | string |
my-r2-bucket |
no |
| Name | Description |
|---|---|
r2_bucket_name |
Name of the R2 bucket |
r2_bucket_id |
ID of the R2 bucket resource |
Reference this repository as a Terraform module in your own configurations:
Option 1: Terraform Registry (recommended)
module "r2" { source = "marcuwynu23/r2/cloudflare" version = "1.0.0" cloudflare_api_token = var.cloudflare_api_token cloudflare_account_id = var.cloudflare_account_id bucket_name = "my-r2-bucket" }Option 2: GitHub source
module "r2" { source = "github.com/marcuwynu23/terraform-cloudflare-r2?ref=main" cloudflare_api_token = var.cloudflare_api_token cloudflare_account_id = var.cloudflare_account_id bucket_name = "my-r2-bucket" }
Then use the outputs in your configuration:
# Example: bind the bucket to a Cloudflare Worker
resource "cloudflare_workers_script" "worker" {
# ...
r2_bucket_bindings {
name = "BUCKET"
bucket_name = module.r2_bucket.r2_bucket_name
}
}All variables and outputs documented below are available when using this as a module.
The cloudflare_r2_bucket resource supports these optional arguments (commented in main.tf):
location— preferred data location. Valid values:wnam,enam,weur,eeur,apac,ocjurisdiction— restrict data jurisdiction. Valid values:default,eu,fedrampstorage_class— default storage class for new objects. Valid values:Standard,InfrequentAccess
Uncomment and set them in main.tf as needed.
After applying, use the output r2_bucket_name in your wrangler.toml:
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-r2-bucket"cloudflare_r2_bucket.this– Cloudflare R2 object storage bucket
terraform.tfvarsand*.tfstatefiles are gitignored — never commit secrets- The API token variable is marked
sensitiveto prevent accidental log exposure - Use a scoped API token (R2 Edit only) rather than a Global API Key
MIT
-
Create a GCS bucket for Terraform remote state:
gcloud storage buckets create gs://your-terraform-state-bucket \ --location=us-central1 \ --uniform-bucket-level-access
-
Create a service account with necessary permissions and generate a JSON key:
- GCP Console → IAM & Admin → Service Accounts → Create Service Account
- Grant the required roles for this module
- Keys → Add Key → Create New Key → JSON
- Copy the entire JSON file contents
-
Add GitHub secrets:
Secret Name Value GCP_SA_KEYFull JSON key from step 2 TF_BUCKET_NAMEYour GCS bucket name TF_BUCKET_PREFIXBucket prefix/path (e.g., cloudflare-r2) -
Run the workflow:
- Apply: Go to Actions → CD - Cloudflare R2 (Apply) → fill in all inputs
- Destroy: Go to Actions → CD - Cloudflare R2 (Destroy) → fill in essential inputs
Alternatively, create a
backend.tfvarsfrombackend.tfvars.exampleand runterraform init -backend-config="backend.tfvars"for local use.
This module uses Google Cloud Storage (GCS) as the Terraform backend for remote state management:
terraform {
backend "gcs" {
bucket = "your-terraform-state-bucket"
prefix = "cloudflare-r2"
}
}Create a backend.tfvars file based on backend.tfvars.example and initialize:
terraform init -backend-config="backend.tfvars"