Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cloudflare-ts-cdn-waf/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin/
/node_modules/
12 changes: 12 additions & 0 deletions cloudflare-ts-cdn-waf/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: cloudflare-ts-cdn-waf
runtime: nodejs
description: Put Cloudflare's CDN and WAF in front of an origin server
template:
config:
zoneId:
description: The Cloudflare zone ID to configure
origin:
description: The origin hostname to proxy traffic to
hostname:
description: The hostname to serve through Cloudflare
default: www
56 changes: 56 additions & 0 deletions cloudflare-ts-cdn-waf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[![Deploy this example with Pulumi](https://www.pulumi.com/images/deploy-with-pulumi/dark.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-cdn-waf/README.md#gh-light-mode-only)
[![Deploy this example with Pulumi](https://get.pulumi.com/new/button-light.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-cdn-waf/README.md#gh-dark-mode-only)

# Cloudflare CDN and WAF in front of an origin

Puts Cloudflare in front of an existing origin server: a proxied DNS record routes traffic
through Cloudflare, a [cache ruleset](https://developers.cloudflare.com/cache/how-to/cache-rules/)
caches responses at the edge, and a [rate-limiting ruleset](https://developers.cloudflare.com/waf/rate-limiting-rules/)
protects the origin from abuse.

## Prerequisites

1. [Install Pulumi](https://www.pulumi.com/docs/install/)
1. [Install Node.js](https://www.pulumi.com/docs/iac/languages-sdks/javascript/)
1. A domain already added to Cloudflare as a [zone](https://developers.cloudflare.com/dns/zone-setups/).
1. Create a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/)
with DNS and Zone WAF edit permissions, and export it:

```bash
export CLOUDFLARE_API_TOKEN=<your-token>
```

## Deploying the example

1. Create a new stack:

```bash
pulumi stack init dev
```

1. Configure the zone and origin:

```bash
pulumi config set zoneId <your-zone-id>
pulumi config set origin origin.example.com
```

1. Install dependencies and deploy:

```bash
npm install
pulumi up
```

1. The proxied hostname is exported as `url`:

```bash
pulumi stack output url
```

## Cleaning up

```bash
pulumi destroy
pulumi stack rm dev
```
67 changes: 67 additions & 0 deletions cloudflare-ts-cdn-waf/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2016-2026, Pulumi Corporation. All rights reserved.

import * as cloudflare from "@pulumi/cloudflare";
import * as pulumi from "@pulumi/pulumi";

// Import the program's configuration settings.
const config = new pulumi.Config();
const zoneId = config.require("zoneId");
const origin = config.require("origin");
const hostname = config.get("hostname") || "www";

// Look up the zone so we can build the fully-qualified hostname.
const zone = cloudflare.getZoneOutput({ zoneId: zoneId });

// A proxied DNS record, so traffic flows through Cloudflare's CDN and WAF.
const record = new cloudflare.DnsRecord("record", {
zoneId: zoneId,
name: hostname,
type: "CNAME",
content: origin,
ttl: 1,
proxied: true,
});

// Cache responses at the edge, overriding the origin's cache headers.
const cache = new cloudflare.Ruleset("cache", {
zoneId: zoneId,
name: "cache-everything",
kind: "zone",
phase: "http_request_cache_settings",
rules: [{
action: "set_cache_settings",
expression: "true",
enabled: true,
actionParameters: {
cache: true,
edgeTtl: {
mode: "override_origin",
default: 3600,
},
},
}],
});

// Rate-limit requests per IP to protect the origin from abuse.
const rateLimit = new cloudflare.Ruleset("rate-limit", {
zoneId: zoneId,
name: "rate-limit",
kind: "zone",
phase: "http_ratelimit",
rules: [{
action: "block",
expression: "true",
enabled: true,
ratelimit: {
characteristics: ["ip.src", "cf.colo.id"],
// period and mitigationTimeout must be 10 on the Free plan; higher
// values require a paid plan.
period: 10,
requestsPerPeriod: 100,
mitigationTimeout: 10,
},
}],
});

// Export the URL served through Cloudflare.
export const url = pulumi.interpolate`https://${hostname}.${zone.name}`;
13 changes: 13 additions & 0 deletions cloudflare-ts-cdn-waf/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "cloudflare-ts-cdn-waf",
"type": "module",
"devDependencies": {
"@types/node": "22.13.14",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
},
"dependencies": {
"@pulumi/cloudflare": "6.20.0",
"@pulumi/pulumi": "3.228.0"
}
}
24 changes: 24 additions & 0 deletions cloudflare-ts-cdn-waf/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"ts-node": {
"esm": true
},
"compilerOptions": {
// Output
"outDir": "bin",
"sourceMap": true,
// Environment
"target": "ES2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"moduleDetection": "force",
"types": ["node"],
// Type Checking
"strict": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"skipLibCheck": true
},
"files": [
"index.ts"
]
}
2 changes: 2 additions & 0 deletions cloudflare-ts-dns/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin/
/node_modules/
7 changes: 7 additions & 0 deletions cloudflare-ts-dns/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: cloudflare-ts-dns
runtime: nodejs
description: Manage Cloudflare DNS records on a zone
template:
config:
zoneId:
description: The Cloudflare zone ID to manage records in
53 changes: 53 additions & 0 deletions cloudflare-ts-dns/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[![Deploy this example with Pulumi](https://www.pulumi.com/images/deploy-with-pulumi/dark.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-dns/README.md#gh-light-mode-only)
[![Deploy this example with Pulumi](https://get.pulumi.com/new/button-light.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-dns/README.md#gh-dark-mode-only)

# Manage Cloudflare DNS records

Manages a set of [DNS records](https://developers.cloudflare.com/dns/manage-dns-records/) on a
Cloudflare zone — an `A` record, a `CNAME`, and a `TXT` record.

## Prerequisites

1. [Install Pulumi](https://www.pulumi.com/docs/install/)
1. [Install Node.js](https://www.pulumi.com/docs/iac/languages-sdks/javascript/)
1. A domain already added to Cloudflare as a [zone](https://developers.cloudflare.com/dns/zone-setups/).
1. Create a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/)
with DNS edit permissions, and export it:

```bash
export CLOUDFLARE_API_TOKEN=<your-token>
```

## Deploying the example

1. Create a new stack:

```bash
pulumi stack init dev
```

1. Set the zone to manage (find the zone ID on your domain's overview page in the Cloudflare dashboard):

```bash
pulumi config set zoneId <your-zone-id>
```

1. Install dependencies and deploy:

```bash
npm install
pulumi up
```

1. Inspect the created records:

```bash
pulumi stack output records
```

## Cleaning up

```bash
pulumi destroy
pulumi stack rm dev
```
40 changes: 40 additions & 0 deletions cloudflare-ts-dns/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2016-2026, Pulumi Corporation. All rights reserved.

import * as cloudflare from "@pulumi/cloudflare";
import * as pulumi from "@pulumi/pulumi";

// Import the program's configuration settings.
const config = new pulumi.Config();
const zoneId = config.require("zoneId");

// An A record pointing "www" at an origin server.
const www = new cloudflare.DnsRecord("www", {
zoneId: zoneId,
name: "www",
type: "A",
content: "192.0.2.1",
ttl: 3600,
proxied: false,
});

// A CNAME aliasing "docs" to an externally hosted site.
const docs = new cloudflare.DnsRecord("docs", {
zoneId: zoneId,
name: "docs",
type: "CNAME",
content: "hosting.example.com",
ttl: 3600,
proxied: false,
});

// A TXT record, e.g. for domain verification or SPF.
const txt = new cloudflare.DnsRecord("txt", {
zoneId: zoneId,
name: "mail",
type: "TXT",
content: "v=spf1 include:_spf.example.com ~all",
ttl: 3600,
});

// Export the fully-qualified names of the records.
export const records = [www.name, docs.name, txt.name];
13 changes: 13 additions & 0 deletions cloudflare-ts-dns/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "cloudflare-ts-dns",
"type": "module",
"devDependencies": {
"@types/node": "22.13.14",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
},
"dependencies": {
"@pulumi/cloudflare": "6.20.0",
"@pulumi/pulumi": "3.228.0"
}
}
24 changes: 24 additions & 0 deletions cloudflare-ts-dns/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"ts-node": {
"esm": true
},
"compilerOptions": {
// Output
"outDir": "bin",
"sourceMap": true,
// Environment
"target": "ES2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"moduleDetection": "force",
"types": ["node"],
// Type Checking
"strict": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"skipLibCheck": true
},
"files": [
"index.ts"
]
}
2 changes: 2 additions & 0 deletions cloudflare-ts-serverless-d1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin/
/node_modules/
7 changes: 7 additions & 0 deletions cloudflare-ts-serverless-d1/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: cloudflare-ts-serverless-d1
runtime: nodejs
description: A serverless Cloudflare Worker backed by a D1 SQL database
template:
config:
accountId:
description: The Cloudflare account ID to deploy into
60 changes: 60 additions & 0 deletions cloudflare-ts-serverless-d1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
[![Deploy this example with Pulumi](https://www.pulumi.com/images/deploy-with-pulumi/dark.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-serverless-d1/README.md#gh-light-mode-only)
[![Deploy this example with Pulumi](https://get.pulumi.com/new/button-light.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-serverless-d1/README.md#gh-dark-mode-only)

# Serverless Cloudflare Worker backed by a D1 database

A [Cloudflare Worker](https://developers.cloudflare.com/workers/) that records each visit in a
[D1](https://developers.cloudflare.com/d1/) serverless SQL database and returns the running
total. The Worker is uploaded as a version and deployed with `cloudflare.WorkersDeployment`,
and is served on its `*.workers.dev` subdomain.

## Prerequisites

1. [Install Pulumi](https://www.pulumi.com/docs/install/)
1. [Install Node.js](https://www.pulumi.com/docs/iac/languages-sdks/javascript/)
1. Create a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/)
with Workers and D1 edit permissions, and export it:

```bash
export CLOUDFLARE_API_TOKEN=<your-token>
```

## Deploying the example

1. Create a new stack:

```bash
pulumi stack init dev
```

1. Set your Cloudflare account ID:

```bash
pulumi config set accountId <your-account-id>
```

1. Install dependencies:

```bash
npm install
```

1. Run `pulumi up` to deploy:

```bash
pulumi up
```

1. Visit the Worker's URL, refreshing a few times to watch the counter increment:

```bash
curl "$(pulumi stack output url)"
# Hello, world! This page has been visited 1 times.
```

## Cleaning up

```bash
pulumi destroy
pulumi stack rm dev
```
Loading
Loading