Deploy Kong Mesh using Terraform and Konnect

Uses: Kong Mesh

Requirements

Terraform variables

This guide allows you to customise the access token and region used by your manifests.

Create a variables.tf file with the following contents:

variable "konnect_personal_access_token" {
    type    = string
}

variable "region" {
    type    = string
}
Copied to clipboard!

These variables can be provided at runtime, or set using environment variables. Set the following environment variables, replacing kpat_... with your Konnect access token:

export TF_VAR_konnect_personal_access_token="kpat_..." # or "spat_..."
export TF_VAR_region="us"
Copied to clipboard!

Provider Configuration

This guide uses the konnect and konnect-beta Terraform providers.

The konnect provider is the General Availability version. Features initially available in the beta version (konnect-beta) will move to the GA version (konnect) once they are stable and fully tested.

There is a beta version of the provider called konnect-beta that has the latest features.

Mesh resources are available only in the beta provider, but they will be available in the konnect provider shortly.

Create a providers.tf file with the following contents:

terraform {
  required_providers {
    konnect = {
      source = "kong/konnect"
      version = ""
    }
    konnect-beta = {
      source  = "kong/konnect-beta"
      version = ""
    }
  }
}

provider "konnect" {
    personal_access_token = var.konnect_personal_access_token
    server_url            = "https://${var.region}.api.konghq.com"
}

provider "konnect-beta" {
    personal_access_token = var.konnect_personal_access_token
    server_url            = "https://${var.region}.api.konghq.com"
}
Copied to clipboard!

Download the providers using terraform init:

terraform init
Copied to clipboard!

You will see the following message in green text:

Terraform has been successfully initialized!
Copied to clipboard!

Mesh Resources

At this point you have installed and configured the providers successfully. It’s time to create some resources using the Konnect API.

Mesh Control Plane

Create a file named main.tf and add the following to create a Global Control Plane in Konnect:

resource "konnect_mesh_control_plane" "my_meshcontrolplane" {
  provider    = konnect-beta
  name        = "tf-cp"
  description = "A control plane created using terraform"
  labels = {
    "terraform" = "true"
  }
}
Copied to clipboard!

After saving the file, run terraform apply -auto-approve to create the resource.

You should see:

  # konnect_mesh_control_plane.my_meshcontrolplane will be created
  + resource "konnect_mesh_control_plane" "my_meshcontrolplane" {
      + created_at  = (known after apply)
      + description = "A control plane created using terraform"
      + features    = (known after apply)
      + id          = (known after apply)
      + labels      = {
          + "terraform" = "true"
        }
      + name        = "tf-cp"
      + updated_at  = (known after apply)
    }
Copied to clipboard!

Creating a Mesh

Now that there is a Control Plane, we can create a new Mesh.

Notice that the cp_id property is set to the ID of the control plane created in the previous step.

The skip_creating_initial_policies property is set to ["*"] to skip creating the default policies so that all resources in the Mesh are tracked by Terraform.

Add the following to main.tf:

resource "konnect_mesh" "my_mesh" {
  provider = konnect-beta

  name     = "my-mesh"
  type     = "Mesh"
  skip_creating_initial_policies = [ "*" ]

  cp_id    = konnect_mesh_control_plane.my_meshcontrolplane.id
}
Copied to clipboard!

Run terraform apply -auto-approve and watch as Terraform creates a new Mesh in your Control Plane.

Let’s add mTLS to the mesh. Replace the konnect_mesh resource you added in main.tf with the following definition:

resource "konnect_mesh" "my_mesh" {
  provider = konnect-beta

  name     = "my-mesh"
  type     = "Mesh"
  skip_creating_initial_policies = [ "*" ]

  mtls = {
    "backends" = [
      {
        "name" = "ca-1"
        "type" = "builtin"
      }
    ]
    "mode"           = "permissive"
    "enabledBackend" = "ca-1"
  }

  cp_id    = konnect_mesh_control_plane.my_meshcontrolplane.id
}
Copied to clipboard!

Run terraform apply -auto-approve and you will see the Mesh being updated in place:

  # konnect_mesh.my_mesh will be updated in-place
  ~ resource "konnect_mesh" "my_mesh" {
      + mtls                           = {
          + backends = [
              + {
                  + name = "ca-1"
                  + type = "builtin"
                },
            ]
        }
        name                           = "my-mesh"
        # (3 unchanged attributes hidden)
    }
Copied to clipboard!

For full schema of the Mesh resource, see the konnect-beta provider documentation.

Adding an example policy

The Kong Mesh documentation policy examples now contain an additional “Terraform” tab showing a Terraform representation of a policy.

Let’s take an example from MeshTrafficPermission page

Autogenerated labels like “kuma.io/mesh”, “kuma.io/origin” etc. have to be manually added to the resources. This limitation will be removed in the GA release.

Add the following policy to main.tf:

resource "konnect_mesh_traffic_permission" "allow_all" {
 provider = konnect-beta

 type = "MeshTrafficPermission"
 name = "allow-all"
 spec = {
   from = [
     {
       target_ref = {
         kind = "Mesh"
       }
       default = {
         action = "Allow"
       }
     }
   ]
 }
 labels   = {
   "kuma.io/mesh" = konnect_mesh.my_mesh.name
 }

 cp_id    = konnect_mesh_control_plane.my_meshcontrolplane.id
 mesh     = konnect_mesh.my_mesh.name
}


Copied to clipboard!

Run terraform apply -auto-approve to create the policy.

Impact of Renaming Resources

Certain properties (like Mesh name, policy name, etc.) are used as identifiers and changing them will result in a new resource being created and all dependant resources being recreated.

So changing mesh name to another-name

resource "konnect_mesh" "my_mesh" {
  # ...
  name = "another-name"
  # ...
}
Copied to clipboard!

Will result in forced replacement of both mesh and konnect_mesh_traffic_permission resources:

    # konnect_mesh.my_mesh must be replaced
-/+ resource "konnect_mesh" "my_mesh" {
      ~ name                           = "mesh1" -> "another-name" # forces replacement
        # (4 unchanged attributes hidden)
    }

  # konnect_mesh_access_log.konnect_mesh_traffic_permission must be replaced
-/+ resource "konnect_mesh_traffic_permission" "allow_all" {
      ~ creation_time     = "2025-03-13T09:53:00.606442Z" -> (known after apply)
      ~ labels            = {
          ~ "kuma.io/mesh" = "mesh1" -> "another-name"
        }
      ~ mesh              = "mesh1" -> "another-name" # forces replacement
Copied to clipboard!

Deploying a Kubernetes Zone

To deploy a Kubernetes zone you can use any Kubernetes services, in this guide we will use k3d to create a local Kubernetes cluster.

Create a cluster

Create a new k3d cluster:

k3d cluster create tfmink
Copied to clipboard!

Store the tfmink cluster configuration in $KUBECONFIG:

export KUBECONFIG=$(k3d kubeconfig write tfmink)
Copied to clipboard!

In variables.tf add a variable pointing to the Kubeconfig file and configure Helm and Kubernetes providers, and a variable for the zone name:

variable "k8s_cluster_config_path" {
  type        = string
  description = "The location where this cluster's kubeconfig will be saved to."
}

variable "zone_name" {
    type    = string
    default = "tfzone1"
}
Copied to clipboard!

Then set TF_VAR_k8s_cluster_config_path to your kubeconfig value:

export TF_VAR_k8s_cluster_config_path=$KUBECONFIG
Copied to clipboard!

Configure the Kubernetes and Helm providers

Update the providers.tf with the following contents to include the time, kubernetes and helm providers:

terraform {
  required_providers {
    konnect = {
      source = "kong/konnect"
    }
    konnect-beta = {
      source  = "kong/konnect-beta"
      version = ""
    }
    time = {
      source  = "hashicorp/time"
      version = ""
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = ""
    }
    helm = {
      source  = "hashicorp/helm"
      version = ""
    }
  }
}
Copied to clipboard!

Then add some provider configuration blocks to the bottom of providers.tf:

provider "helm" {
    kubernetes {
        config_path = pathexpand(var.k8s_cluster_config_path)
    }
}

provider "kubernetes" {
    config_path = pathexpand(var.k8s_cluster_config_path)
}
Copied to clipboard!

Run init -upgrade to download the new providers:

terraform init -upgrade
Copied to clipboard!

Create a system account

Create a new file named system-account.tf with the following contents to create a system account and a token to authenticate the zone:

resource "konnect_system_account" "zone_system_account" {
  name            = "mesh_${konnect_mesh_control_plane.my_meshcontrolplane.id}_${var.zone_name}"
  description     = "Terraform generated system account for authentication zone ${var.zone_name} in ${konnect_mesh_control_plane.my_meshcontrolplane.id} control plane."
  konnect_managed = false
}

resource "konnect_system_account_role" "zone_system_account_role" {
  account_id       = konnect_system_account.zone_system_account.id
  entity_id        = konnect_mesh_control_plane.my_meshcontrolplane.id
  entity_region    = var.region
  entity_type_name = "Mesh Control Planes"
  role_name        = "Connector"
}

resource "time_offset" "one_year_from_now" {
  offset_years = 1
}

resource "konnect_system_account_access_token" "zone_system_account_token" {
  account_id = konnect_system_account.zone_system_account.id
  expires_at = time_offset.one_year_from_now.rfc3339
  name       = konnect_system_account.zone_system_account.name
}
Copied to clipboard!

Store this token in Kubernetes by creating k8s.tf with the following contents:

resource "kubernetes_namespace" "kong_mesh_system" {
  metadata {
    name = "kong-mesh-system"
    labels = {
      "kuma.io/system-namespace" = "true"
    }
  }
}

resource "kubernetes_secret" "mesh_cp_token" {
  metadata {
    name = "cp-token"
    namespace = kubernetes_namespace.kong_mesh_system.metadata.0.name
  }

  data = {
    token = konnect_system_account_access_token.zone_system_account_token.token
  }

  type = "opaque"
}
Copied to clipboard!

Create a Zone deployment

Create a values file called values.tftpl with templated values for zone, address and control plane id:

kuma:
  controlPlane:
    mode: zone
    zone: ${zone_name}
    kdsGlobalAddress: grpcs://${region}.mesh.sync.konghq.com:443
    konnect:
      cpId: ${cp_id}
    secrets:
      - Env: KMESH_MULTIZONE_ZONE_KDS_AUTH_CP_TOKEN_INLINE
        Secret: cp-token
        Key: token
  ingress:
    enabled: true
  egress:
    enabled: true
Copied to clipboard!

Create a zone.tf file containing the following to create a zone:

resource "helm_release" "kong_mesh" {
  name       = "kong-mesh"
  repository = "https://kong.github.io/kong-mesh-charts"
  chart      = "kong-mesh"
  version    = ""

  namespace = kubernetes_namespace.kong_mesh_system.metadata.0.name
  upgrade_install = true

  values = [templatefile("values.tftpl", {
    zone_name = var.zone_name,
    region    = var.region,
    cp_id     = konnect_mesh_control_plane.my_meshcontrolplane.id
  })]
}
Copied to clipboard!

And finally apply the changes:

terraform apply -auto-approve
Copied to clipboard!

You should see 7 resources created and helm_release can take some time to create. In my case it took 53 seconds:

helm_release.kong_mesh: Creation complete after 53s [id=kong-mesh]
Copied to clipboard!

You can check that the MeshTrafficPermission we created earlier is now available in the zone:

kubectl get meshtrafficpermissions.kuma.io -A
Copied to clipboard!

should print:

NAMESPACE          NAME
kong-mesh-system   allow-all-wd5xx76vc44b498c
Copied to clipboard!

Cleaning up

Congratulations! You just deployed a Mesh Control Plane, Mesh, policies and a Zone to Kubernetes.

To clean up all resources created by this guide, run the following command:

terraform destroy
Copied to clipboard!

Did this doc help?

Something wrong?

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!