Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

Terraform

最終更新 Markdown で表示Agent セットアップ

Terraform は Infrastructure as Code のツールです。トンネルをほかのインフラと一緒に定義し、管理できます。このガイドでは次をデプロイします。

  • Web サーバーを動かす GCP 仮想マシン
  • そのサーバーをインターネットへ公開する Cloudflare Tunnel
  • (任意)接続できるユーザーを定義する Cloudflare Access ポリシー

前提条件

1. Terraform をインストールする

お使いの OS 向けの Terraform インストールガイド を参照してください。

2. gcloud CLI をインストールする

  1. Terraform が GCP アカウントと連携できるように、gcloud CLI をインストール します。

  2. 次を実行して CLI で認証します。

    gcloud auth application-default login

3. Cloudflare API トークンを作成する

Terraform が Cloudflare アカウントと連携できるように、API トークンを作成 します。トークンには、少なくとも次の権限を含めます。

種類 項目 権限
Account Cloudflare Tunnel Edit
Account Access: Apps and Policies Edit
Zone DNS Edit

4. 設定ディレクトリを作成する

Terraform は、設定ファイルを含む作業ディレクトリで動作します。設定は複数のファイルに分けても、1 つのファイルにまとめても構いません。Terraform はディレクトリ内のすべての設定ファイルを、1 つのドキュメントとして評価します。

  1. Terraform 設定用のフォルダーを作成します。

    mkdir cloudflare-tf
  2. そのディレクトリに移動します。

    cd cloudflare-tf

5. Terraform 設定ファイルを作成する

入力変数を定義する

次の変数は、GCP と Cloudflare の構成に渡されます。

  1. 構成ディレクトリに .tf ファイルを作成します。

    touch variables.tf
  2. テキストエディターでファイルを開き、次の内容をコピーして貼り付けます。

    # GCP variables
    variable "gcp_project_id" {
      description = "Google Cloud Platform (GCP) project ID"
      type        = string
    }
    
    variable "zone" {
      description = "Geographical zone for the GCP VM instance"
      type        = string
    }
    
    variable "machine_type" {
      description = "Machine type for the GCP VM instance"
      type        = string
    }
    
    # Cloudflare variables
    variable "cloudflare_zone" {
      description = "Domain used to expose the GCP VM instance to the Internet"
      type        = string
    }
    
    variable "cloudflare_zone_id" {
      description = "Zone ID for your domain"
      type        = string
    }
    
    variable "cloudflare_account_id" {
      description = "Account ID for your Cloudflare account"
      type        = string
      sensitive   = true
    }
    
    variable "cloudflare_email" {
      description = "Email address for your Cloudflare account"
      type        = string
      sensitive   = true
    }
    
    variable "cloudflare_token" {
      description = "Cloudflare API token"
      type        = string
      sensitive   = true
    }

変数に値を割り当てる

  1. 設定ディレクトリに .tfvars ファイルを作成します。

    touch terraform.tfvars

    ファイル名が terraform.tfvars であれば、Terraform はこれらの変数を自動的に使います。それ以外の名前の場合は、変数ファイルを手動で渡す必要があります。

  2. 次の変数を terraform.tfvars に追加します。例は自分の値に書き換えてください。

    cloudflare_zone           = "example.com"
    cloudflare_zone_id        = "023e105f4ecef8ad9ca31a8372d0c353"
    cloudflare_account_id     = "372e67954025e0ba6aaa6d586b9e0b59"
    cloudflare_email          = "[email protected]"
    cloudflare_token          = "y3AalHS_E7Vabk3c3lX950F90_Xl7YtjSlzyFn_X"
    gcp_project_id            = "testvm-123"
    zone                      = "us-central1-a"
    machine_type              = "e2-medium"

Terraform プロバイダーを設定する

インフラストラクチャのプロビジョニングに使う プロバイダー を宣言します。

  1. 設定ディレクトリに .tf ファイルを作成します。

    touch providers.tf
  2. 次のプロバイダーを providers.tf に追加します。random プロバイダーはトンネルシークレットの生成に使います。

    terraform {
    	required_providers {
    		cloudflare = {
    			source = "cloudflare/cloudflare"
    			version = ">= 5.8.2"
    		}
    		google = {
    			source = "hashicorp/google"
    		}
    	}
    	required_version = ">= 1.2"
    }
    
    # Providers
    provider "cloudflare" {
    	api_token    = var.cloudflare_token
    }
    provider "google" {
    	project    = var.gcp_project_id
    }
    provider "random" {
    }
    terraform {
    	required_providers {
    		cloudflare = {
    			source = "cloudflare/cloudflare"
    			version = ">= 4.40.0, < 5.0.0"
    		}
    		google = {
    			source = "hashicorp/google"
    		}
    		random = {
    			source = "hashicorp/random"
    		}
    	}
    	required_version = ">= 1.2"
    }
    
    # Providers
    provider "cloudflare" {
    	api_token    = var.cloudflare_token
    }
    provider "google" {
    	project    = var.gcp_project_id
    }
    provider "random" {
    }

Cloudflare リソースを設定する

次の設定は、Cloudflare アカウントの設定を変更します。

  1. 設定ディレクトリに .tf ファイルを作成します。

    touch Cloudflare-config.tf
  2. Cloudflare-config.tf に次のリソースを追加します。

    
    # Creates a new remotely-managed tunnel for the GCP VM.
    resource "cloudflare_zero_trust_tunnel_cloudflared" "gcp_tunnel" {
    	account_id    = var.cloudflare_account_id
    	name          = "Terraform GCP tunnel"
    	config_src    = "cloudflare"
    }
    
    # Reads the token used to run the tunnel on the server.
    data "cloudflare_zero_trust_tunnel_cloudflared_token" "gcp_tunnel_token" {
    	account_id 	= var.cloudflare_account_id
    	tunnel_id 	= cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    }
    
    # Creates the CNAME record that routes http_app.${var.cloudflare_zone} to the tunnel.
    resource "cloudflare_dns_record" "http_app" {
    	zone_id = var.cloudflare_zone_id
    	name    = "http_app"
    	content = "${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id}.cfargotunnel.com"
    	type    = "CNAME"
    	ttl     = 1
    	proxied = true
    }
    
    # Configures tunnel with a published application for clientless access.
    resource "cloudflare_zero_trust_tunnel_cloudflared_config" "gcp_tunnel_config" {
    	tunnel_id  = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    	account_id = var.cloudflare_account_id
    	config     = {
    		ingress 	= [
    			{
    				hostname = "http_app.${var.cloudflare_zone}"
    				service  = "http://httpbin:80"
    			},
    			{
    				service  = "http_status:404"
    			}
    		]
    	}
    }
    
    # (Optional) Routes internal IP of GCP instance through the tunnel for private network access using WARP.
    resource "cloudflare_zero_trust_tunnel_cloudflared_route" "example_tunnel_route" {
    account_id         = var.cloudflare_account_id
    tunnel_id          = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    network            = google_compute_instance.http_server.network_interface.0.network_ip
    comment            = "Example tunnel route"
    }
    
    # Creates a reusable Access policy.
    resource "cloudflare_zero_trust_access_policy" "allow_emails" {
    	account_id   = var.cloudflare_account_id
    	name         = "Allow email addresses"
    	decision     = "allow"
    	include      = [
    		{
    			email = {
    				email = var.cloudflare_email
    			}
    		},
    		{
    			email_domain = {
    				domain = "@example.com"
    			}
    		}
    	]
    }
    
    # Creates an Access application to control who can connect to the public hostname.
    resource "cloudflare_zero_trust_access_application" "http_app" {
    	account_id       = var.cloudflare_account_id
    	type             = "self_hosted"
    	name             = "Access application for http_app.${var.cloudflare_zone}"
    	domain           = "http_app.${var.cloudflare_zone}"
    	policies = [
    		{
    			id = cloudflare_zero_trust_access_policy.allow_emails.id
    			precedence = 1
    		}
    	]
    }
    # Generates a 32-byte secret for the tunnel.
    resource "random_bytes" "tunnel_secret" {
    	byte_length = 32
    }
    
    # Creates a new remotely-managed tunnel for the GCP VM.
    resource "cloudflare_zero_trust_tunnel_cloudflared" "gcp_tunnel" {
    	account_id = var.cloudflare_account_id
    	name       = "Terraform GCP tunnel"
    	secret     = random_bytes.tunnel_secret.base64
    }
    
    # Creates the CNAME record that routes http_app.${var.cloudflare_zone} to the tunnel.
    resource "cloudflare_record" "http_app" {
    	zone_id = var.cloudflare_zone_id
    	name    = "http_app"
    	content   = "${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.cname}"
    	type    = "CNAME"
    	proxied = true
    }
    
    # Configures tunnel with a published application for clientless access.
    resource "cloudflare_zero_trust_tunnel_cloudflared_config" "gcp_tunnel_config" {
    	tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    	account_id = var.cloudflare_account_id
    	config {
    		ingress_rule {
    			hostname = "${cloudflare_record.http_app.hostname}"
    			service  = "http://httpbin:80"
    		}
    		ingress_rule {
    			service  = "http_status:404"
    		}
    	}
    }
    
    # (Optional) Route internal IP of GCP instance through the tunnel for private network access using WARP.
    resource "cloudflare_zero_trust_tunnel_route" "example_tunnel_route" {
    account_id         = var.cloudflare_account_id
    tunnel_id          = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    network            = google_compute_instance.http_server.network_interface.0.network_ip
    comment            = "Example tunnel route"
    }
    
    # Creates an Access application to control who can connect to the public hostname.
    resource "cloudflare_zero_trust_access_application" "http_app" {
    	account_id          = var.cloudflare_account_id
    	name             = "Access application for http_app.${var.cloudflare_zone}"
    	domain           = "http_app.${var.cloudflare_zone}"
    }
    
    # Creates a (legacy) Access policy for the Access application.
    resource "cloudflare_zero_trust_access_policy" "allow_emails" {
    	application_id = cloudflare_zero_trust_access_application.http_app.id
    	account_id        = var.cloudflare_account_id
    	name           = "Example policy for http_app.${var.cloudflare_zone}"
    	precedence     = "1"
    	decision       = "allow"
    	include {
    		email = [var.cloudflare_email]
    	}
    }

これらのリソースの詳細は、Cloudflare provider のドキュメント を参照してください。

GCP リソースを設定する

次の構成は、GCP 仮想マシンの仕様を定義し、起動時に実行するスタートアップスクリプトを設定します。

  1. 構成ディレクトリに .tf ファイルを作成します。

    touch GCP-config.tf
  2. 次の内容を GCP-config.tf に追加します。

    # OS the server will use
    data "google_compute_image" "image" {
    	family  = "ubuntu-2204-lts"
    	project = "ubuntu-os-cloud"
    }
    
    # GCP Instance resource
    resource "google_compute_instance" "http_server" {
    	name         = "test"
    	machine_type = var.machine_type
    	zone         = var.zone
    	tags         = []
    	boot_disk {
    		initialize_params {
    			image = data.google_compute_image.image.self_link
    		}
    	}
    
    	network_interface {
    		network = "default"
    		access_config {
    			//Ephemeral IP
    		}
    	}
    	// Optional config to make instance ephemeral
    /*  scheduling {
    		preemptible       = true
    		automatic_restart = false
    	} */
    
    	// Pass the tunnel token to the GCP server so that the server can install and run the tunnel upon startup.
    	metadata_startup_script = templatefile("./install-tunnel.tftpl",
    		{
    			tunnel_token = data.cloudflare_zero_trust_tunnel_cloudflared_token.gcp_tunnel_token.token
    		})
    }
    # OS the server will use
    data "google_compute_image" "image" {
    	family  = "ubuntu-2204-lts"
    	project = "ubuntu-os-cloud"
    }
    
    # GCP Instance resource
    resource "google_compute_instance" "http_server" {
    	name         = "test"
    	machine_type = var.machine_type
    	zone         = var.zone
    	tags         = []
    	boot_disk {
    		initialize_params {
    			image = data.google_compute_image.image.self_link
    		}
    	}
    
    	network_interface {
    		network = "default"
    		access_config {
    			//Ephemeral IP
    		}
    	}
    	// Optional config to make instance ephemeral
    /*  scheduling {
    		preemptible       = true
    		automatic_restart = false
    	} */
    
    	// Pass the tunnel token to the GCP server so that the server can install and run the tunnel upon startup.
    	metadata_startup_script = templatefile("./install-tunnel.tftpl",
    		{
    			tunnel_token = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.tunnel_token
    		})
    }

スタートアップスクリプトを作成する

次のスクリプトは cloudflared をインストールし、トンネルをサービスとして起動します。この例では、接続確認に使える軽量な HTTP アプリケーションもインストールします。

  1. 設定ディレクトリに Terraform テンプレートファイルを作成します。

    touch install-tunnel.tftpl
  2. テキストエディターでファイルを開き、次の bash スクリプトをコピーして貼り付けます。

    # Script to install Cloudflare Tunnel and Docker resources
    
    # Docker configuration
    cd /tmp
    sudo apt-get install software-properties-common
    # Retrieving the docker repository for this OS
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
    # The OS is updated and docker is installed
    sudo apt update -y && sudo apt upgrade -y
    sudo apt install docker docker-compose -y
    # Add the HTTPBin application and run it on localhost:8080.
    cat > /tmp/docker-compose.yml << "EOF"
    version: '3'
    services:
      httpbin:
        image: kennethreitz/httpbin
        restart: always
        container_name: httpbin
        ports:
          - 8080:80
    
      cloudflared:
        image: cloudflare/cloudflared:latest
        restart: always
        container_name: cloudflared
        command: tunnel run --token ${tunnel_token}
    EOF
    cd /tmp
    sudo docker-compose up -d

6. Terraform をデプロイする

構成ファイルをデプロイするには:

  1. 構成ディレクトリを初期化します。

    terraform init
  2. 作成される内容をプレビューします。

    terraform plan
  3. 構成を適用します。

    terraform apply

GCP インスタンスとトンネルがオンラインになるまで、数分かかることがあります。新しいトンネルは、Cloudflare ダッシュボードNetworking > Tunnels で確認できます。

7. 接続をテストする

  1. Cloudflare ダッシュボードNetworking > Tunnels を開き、トンネルが Active であることを確認します。

  2. (任意)Access を設定した場合は、Security > Access > Applications を開き、自分の Cloudflare メールが Access ポリシーで許可されていることを確認します。

  3. 任意の端末のブラウザーで http_app.<CLOUDFLARE_ZONE> を開きます(例: http_app.example.com)。

    Access を設定している場合は、Access のログインページが表示されます。Cloudflare のメールでログインします。

  4. HTTPBin のホームページが表示されれば、トンネルが正しくトラフィックをルーティングしています。

役に立ちましたか?