Search code examples
terraformcloudflareterraform-provider-cloudflare

terraform provisioning locally cloudflared tunnel


I tried to use terraform without any Cloud instance - only for local install cloudflared tunnel using construction:

    resource "null_resource" "tunell_install" {
  triggers = {
    always_run = timestamp()
  }
  provisioner "local-exec" {
    command = "/home/uzer/script/tunnel.sh"
  }
}

instead something like:

provider "google" {
  project    = var.gcp_project_id
}

but after running

$ terraform apply -auto-approve

successfully created /etc/cloudflared/cert.json with content:

{
    "AccountTag"   : "${account}",
    "TunnelID"     : "${tunnel_id}",
    "TunnelName"   : "${tunnel_name}",
    "TunnelSecret" : "${secret}"
}

but as I undestood here must be values instead variables? It's seems that metadata_startup_script from instance.tf only applied to Google instance. How it's possible to change it for using terraform with install CF tunnel locally and running tunnel? Maybe also need to use templatefile but in other .tf file? The curent code block metadata_startup_script:

  // This is where we configure the server (aka instance). Variables like web_zone take a terraform variable and provide it to the server so that it can use them as a local variable
  metadata_startup_script = templatefile("./server.tpl",
    {
      web_zone    = var.cloudflare_zone,
      account     = var.cloudflare_account_id,
      tunnel_id   = cloudflare_argo_tunnel.auto_tunnel.id,
      tunnel_name = cloudflare_argo_tunnel.auto_tunnel.name,
      secret      = random_id.tunnel_secret.b64_std
    })

Content of server.tpl file:

# Script to install Cloudflare Tunnel 
# cloudflared configuration
cd
# The package for this OS is retrieved 
wget https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-amd64.deb
sudo dpkg -i cloudflared-stable-linux-amd64.deb
# A local user directory is first created before we can install the tunnel as a system service 
mkdir ~/.cloudflared
touch ~/.cloudflared/cert.json
touch ~/.cloudflared/config.yml
# Another herefile is used to dynamically populate the JSON credentials file 
cat > ~/.cloudflared/cert.json << "EOF"
{
    "AccountTag"   : "${account}",
    "TunnelID"     : "${tunnel_id}",
    "TunnelName"   : "${tunnel_name}",
    "TunnelSecret" : "${secret}"
}
EOF
# Same concept with the Ingress Rules the tunnel will use 
cat > ~/.cloudflared/config.yml << "EOF"
tunnel: ${tunnel_id}
credentials-file: /etc/cloudflared/cert.json
logfile: /var/log/cloudflared.log
loglevel: info

ingress:
  - hostname: ssh.${web_zone}
    service: ssh://localhost:22
  - hostname: "*"
    service: hello-world
EOF
# Now we install the tunnel as a systemd service 
sudo cloudflared service install
# The credentials file does not get copied over so we'll do that manually 
sudo cp -via ~/.cloudflared/cert.json /etc/cloudflared/
# Now we can start the tunnel 
sudo service cloudflared start

In argo.tf exist this code:

data "template_file" "init" {
  template = file("server.tpl")
  vars = {
    web_zone    = var.cloudflare_zone,
    account     = var.cloudflare_account_id,
    tunnel_id   = cloudflare_argo_tunnel.auto_tunnel.id,
    tunnel_name = cloudflare_argo_tunnel.auto_tunnel.name,
    secret      = random_id.tunnel_secret.b64_std
  }
}

Solution

  • If you are asking about how to create the file locally and populate the values, here is an example:

    resource "local_file" "cloudflare_tunnel_script" {
      content = templatefile("${path.module}/server.tpl",
        {
          web_zone    = "webzone"
          account     = "account"
          tunnel_id   = "id"
          tunnel_name = "name"
          secret      = "secret"
        }
      )
      filename = "${path.module}/server.sh"
    }
    

    For this to work, you would have to assign the real values for all the template variables listed above. From what I see, there are already examples of how to use variables for those values. In other words, instead of hardcoding the values for template variables you could use standard variables:

    resource "local_file" "cloudflare_tunnel_script" {
      content = templatefile("${path.module}/server.tpl",
        {
          web_zone    = var.cloudflare_zone
          account     = var.cloudflare_account_id
          tunnel_id   = cloudflare_argo_tunnel.auto_tunnel.id
          tunnel_name = cloudflare_argo_tunnel.auto_tunnel.name
          secret      = random_id.tunnel_secret.b64_std
        }
      )
      filename = "${path.module}/server.sh"
    }
    

    This code will populate all the values and create a server.sh script in the same directory you are running the Terraform code from.

    You could complement this code with the null_resource you wanted:

    resource "null_resource" "tunnel_install" {
      depends_on = [
        local_file.cloudflare_tunnel_script,
      ]
    
      triggers = {
        always_run = timestamp()
      }
    
      provisioner "local-exec" {
        command = "${path.module}/server.sh"
      }
    }