mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
# Terraform Patterns & Use Cases
|
||||
|
||||
Architecture patterns, multi-environment setups, and real-world use cases.
|
||||
|
||||
## Recommended Directory Structure
|
||||
|
||||
```
|
||||
terraform/
|
||||
├── environments/
|
||||
│ ├── production/
|
||||
│ │ ├── main.tf
|
||||
│ │ └── terraform.tfvars
|
||||
│ └── staging/
|
||||
│ ├── main.tf
|
||||
│ └── terraform.tfvars
|
||||
├── modules/
|
||||
│ ├── zone/
|
||||
│ ├── worker/
|
||||
│ └── dns/
|
||||
└── shared/ # Shared resources across envs
|
||||
└── main.tf
|
||||
```
|
||||
|
||||
**Note:** Cloudflare recommends avoiding modules for provider resources due to v5 auto-generation complexity. Prefer environment directories + shared state instead.
|
||||
|
||||
## Multi-Environment Setup
|
||||
|
||||
```hcl
|
||||
# Directory: environments/{production,staging}/main.tf + modules/{zone,worker,pages}
|
||||
module "zone" {
|
||||
source = "../../modules/zone"; account_id = var.account_id; zone_name = "example.com"; environment = "production"
|
||||
}
|
||||
module "api_worker" {
|
||||
source = "../../modules/worker"; account_id = var.account_id; zone_id = module.zone.zone_id
|
||||
name = "api-worker-prod"; script = file("../../workers/api.js"); environment = "production"
|
||||
}
|
||||
```
|
||||
|
||||
## R2 State Backend
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
backend "s3" {
|
||||
bucket = "terraform-state"
|
||||
key = "cloudflare.tfstate"
|
||||
region = "auto"
|
||||
endpoints = { s3 = "https://<account_id>.r2.cloudflarestorage.com" }
|
||||
skip_credentials_validation = true
|
||||
skip_region_validation = true
|
||||
skip_requesting_account_id = true
|
||||
skip_metadata_api_check = true
|
||||
skip_s3_checksum = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Worker with All Bindings
|
||||
|
||||
```hcl
|
||||
locals { worker_name = "full-stack-worker" }
|
||||
resource "cloudflare_workers_kv_namespace" "app" { account_id = var.account_id; title = "${local.worker_name}-kv" }
|
||||
resource "cloudflare_r2_bucket" "app" { account_id = var.account_id; name = "${local.worker_name}-bucket" }
|
||||
resource "cloudflare_d1_database" "app" { account_id = var.account_id; name = "${local.worker_name}-db" }
|
||||
|
||||
resource "cloudflare_worker_script" "app" {
|
||||
account_id = var.account_id; name = local.worker_name; content = file("worker.js"); module = true
|
||||
compatibility_date = "2025-01-01"
|
||||
kv_namespace_binding { name = "KV"; namespace_id = cloudflare_workers_kv_namespace.app.id }
|
||||
r2_bucket_binding { name = "BUCKET"; bucket_name = cloudflare_r2_bucket.app.name }
|
||||
d1_database_binding { name = "DB"; database_id = cloudflare_d1_database.app.id }
|
||||
secret_text_binding { name = "API_KEY"; text = var.api_key }
|
||||
}
|
||||
```
|
||||
|
||||
## Wrangler Integration
|
||||
|
||||
**CRITICAL**: Wrangler and Terraform must NOT manage same resources.
|
||||
|
||||
**Terraform**: Zones, DNS, security rules, Access, load balancers, worker deployments (CI/CD), KV/R2/D1 resource creation
|
||||
**Wrangler**: Local dev (`wrangler dev`), manual deploys, D1 migrations, KV bulk ops, log streaming (`wrangler tail`)
|
||||
|
||||
### CI/CD Pattern
|
||||
|
||||
```hcl
|
||||
# Terraform creates infrastructure
|
||||
resource "cloudflare_workers_kv_namespace" "app" { account_id = var.account_id; title = "app-kv" }
|
||||
resource "cloudflare_d1_database" "app" { account_id = var.account_id; name = "app-db" }
|
||||
output "kv_namespace_id" { value = cloudflare_workers_kv_namespace.app.id }
|
||||
output "d1_database_id" { value = cloudflare_d1_database.app.id }
|
||||
```
|
||||
|
||||
```yaml
|
||||
# GitHub Actions: terraform apply → envsubst wrangler.jsonc.template → wrangler deploy
|
||||
- run: terraform apply -auto-approve
|
||||
- run: |
|
||||
export KV_NAMESPACE_ID=$(terraform output -raw kv_namespace_id)
|
||||
envsubst < wrangler.jsonc.template > wrangler.jsonc
|
||||
- run: wrangler deploy
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Static Site + API Worker
|
||||
|
||||
```hcl
|
||||
resource "cloudflare_pages_project" "frontend" {
|
||||
account_id = var.account_id; name = "frontend"; production_branch = "main"
|
||||
build_config { build_command = "npm run build"; destination_dir = "dist" }
|
||||
}
|
||||
resource "cloudflare_worker_script" "api" {
|
||||
account_id = var.account_id; name = "api"; content = file("api-worker.js")
|
||||
d1_database_binding { name = "DB"; database_id = cloudflare_d1_database.api_db.id }
|
||||
}
|
||||
resource "cloudflare_dns_record" "frontend" {
|
||||
zone_id = cloudflare_zone.main.id; name = "app"; content = cloudflare_pages_project.frontend.subdomain; type = "CNAME"; proxied = true
|
||||
}
|
||||
resource "cloudflare_worker_route" "api" {
|
||||
zone_id = cloudflare_zone.main.id; pattern = "api.example.com/*"; script_name = cloudflare_worker_script.api.name
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Region Load Balancing
|
||||
|
||||
```hcl
|
||||
resource "cloudflare_load_balancer_pool" "us" {
|
||||
account_id = var.account_id; name = "us-pool"; monitor = cloudflare_load_balancer_monitor.http.id
|
||||
origins { name = "us-east"; address = var.us_east_ip }
|
||||
}
|
||||
resource "cloudflare_load_balancer_pool" "eu" {
|
||||
account_id = var.account_id; name = "eu-pool"; monitor = cloudflare_load_balancer_monitor.http.id
|
||||
origins { name = "eu-west"; address = var.eu_west_ip }
|
||||
}
|
||||
resource "cloudflare_load_balancer" "global" {
|
||||
zone_id = cloudflare_zone.main.id; name = "api.example.com"; steering_policy = "geo"
|
||||
default_pool_ids = [cloudflare_load_balancer_pool.us.id]
|
||||
region_pools { region = "WNAM"; pool_ids = [cloudflare_load_balancer_pool.us.id] }
|
||||
region_pools { region = "WEU"; pool_ids = [cloudflare_load_balancer_pool.eu.id] }
|
||||
}
|
||||
```
|
||||
|
||||
### Secure Admin with Access
|
||||
|
||||
```hcl
|
||||
resource "cloudflare_pages_project" "admin" { account_id = var.account_id; name = "admin"; production_branch = "main" }
|
||||
resource "cloudflare_access_application" "admin" {
|
||||
account_id = var.account_id; name = "Admin"; domain = "admin.example.com"; type = "self_hosted"; session_duration = "24h"
|
||||
allowed_idps = [cloudflare_access_identity_provider.google.id]
|
||||
}
|
||||
resource "cloudflare_access_policy" "allow" {
|
||||
account_id = var.account_id; application_id = cloudflare_access_application.admin.id
|
||||
name = "Allow admins"; decision = "allow"; precedence = 1; include { email = var.admin_emails }
|
||||
}
|
||||
```
|
||||
|
||||
### Reusable Module
|
||||
|
||||
```hcl
|
||||
# modules/cloudflare-zone/main.tf
|
||||
variable "account_id" { type = string }; variable "domain" { type = string }; variable "ssl_mode" { default = "strict" }
|
||||
resource "cloudflare_zone" "main" { account = { id = var.account_id }; name = var.domain }
|
||||
resource "cloudflare_zone_settings_override" "main" {
|
||||
zone_id = cloudflare_zone.main.id; settings { ssl = var.ssl_mode; always_use_https = "on" }
|
||||
}
|
||||
output "zone_id" { value = cloudflare_zone.main.id }
|
||||
|
||||
# Usage: module "prod" { source = "./modules/cloudflare-zone"; account_id = var.account_id; domain = "example.com" }
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [README](./README.md) - Provider setup
|
||||
- [Configuration Reference](./configuration.md) - All resource types
|
||||
- [API Reference](./api.md) - Data sources
|
||||
- [Troubleshooting](./gotchas.md) - Best practices, common issues
|
||||
Reference in New Issue
Block a user