This guide uses Terraform to provision a Konnect-managed global control plane, a mesh with mTLS, and a MeshTrafficPermission policy, and then deploys a Kubernetes zone control plane that connects to it. To learn how Kong Mesh works with Konnect, see Kong Mesh in Konnect.
Use the konnect and konnect-beta Terraform providers to create a global control plane, a mesh, and a MeshTrafficPermission policy in Konnect, then deploy a Kubernetes zone control plane that authenticates with a system account token.
Prerequisites
Kong Konnect
If you don’t have a Konnect account, you can get started quickly with our onboarding wizard.
- The following Konnect items are required to complete this tutorial:
- Personal access token (PAT): Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.
-
Set the personal access token as an environment variable:
export KONNECT_TOKEN='YOUR KONNECT TOKEN'
Terraform
This how-to requires you to install Terraform.
Set up the Terraform variables
-
Create and enter a working directory for this guide:
mkdir -p ~/mesh-konnect && cd ~/mesh-konnect -
Create a
variables.tffile:cat <<'EOF' > variables.tf variable "konnect_personal_access_token" { type = string } variable "region" { type = string } EOF -
Provide these variables at runtime, or set them as environment variables:
export TF_VAR_konnect_personal_access_token=$KONNECT_TOKEN export TF_VAR_region="us"
Configure the providers
This guide uses the konnect and konnect-beta Terraform providers. The konnect provider is the general availability version, and konnect-beta is the beta version with the latest features. Features move from the beta provider to the GA provider once they are stable and fully tested.
Mesh resources are currently available only in the beta provider.
-
Create a
providers.tffile:cat <<'EOF' > providers.tf terraform { required_providers { konnect = { source = "kong/konnect" } konnect-beta = { source = "kong/konnect-beta" } } } 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" } EOF -
Download the providers:
terraform initYou should see the following message:
Terraform has been successfully initialized!
Create a global control plane in Konnect
-
Create a
main.tffile that defines a global control plane in Konnect:cat <<'EOF' > main.tf resource "konnect_mesh_control_plane" "my_meshcontrolplane" { provider = konnect-beta name = "tf-cp" description = "A control plane created using terraform" labels = { "terraform" = "true" } } EOF -
Apply the changes to create the resource:
terraform apply -auto-approveYou should see:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # 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) } Plan: 1 to add, 0 to change, 0 to destroy. konnect_mesh_control_plane.my_meshcontrolplane: Creating... konnect_mesh_control_plane.my_meshcontrolplane: Creation complete after 1s Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Create a mesh
Now that a control plane exists, create a mesh with mTLS enabled. The cp_id property is set to the ID of the control plane created in the previous step, and skip_creating_initial_policies is set to ["*"] to skip creating the default policies so that all resources in the mesh are tracked by Terraform.
-
Create a
mesh.tffile:cat <<'EOF' > mesh.tf 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 } EOF -
Apply the changes to create the mesh:
terraform apply -auto-approve
For the full schema of the mesh resource, see the konnect-beta provider documentation.
Add a traffic permission policy
Each Kong Mesh policy example includes a Terraform tab showing a Terraform representation of the policy. This step uses the allow-all example from the MeshTrafficPermission page.
Kong Mesh manages reserved
kuma.io/*labels, such askuma.io/meshandkuma.io/origin, automatically. Don’t set them in your Terraform configuration: themeshattribute already associates the policy with its mesh, and setting a reserved label causes a provider plan error.
-
Create a
traffic-permission.tffile:cat <<'EOF' > traffic-permission.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" } } ] } cp_id = konnect_mesh_control_plane.my_meshcontrolplane.id mesh = konnect_mesh.my_mesh.name } EOF -
Apply the changes to create the policy:
terraform apply -auto-approve
Deploy a Kubernetes zone
You can deploy a Kubernetes zone to any Kubernetes cluster. This guide uses k3d to create a local cluster.
Create a cluster
-
Create a new k3d cluster:
k3d cluster create tfmink -
Store the
tfminkcluster configuration in$KUBECONFIG:export KUBECONFIG=$(k3d kubeconfig write tfmink) -
Add a variable pointing to the kubeconfig file and a variable for the zone name to
variables.tf:cat <<'EOF' >> variables.tf 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" } EOF -
Set
TF_VAR_k8s_cluster_config_pathto your kubeconfig value:export TF_VAR_k8s_cluster_config_path=$KUBECONFIG
Configure the Kubernetes and Helm providers
-
Update
providers.tfto add thetime,kubernetes, andhelmproviders:cat <<'EOF' > providers.tf terraform { required_providers { konnect = { source = "kong/konnect" } konnect-beta = { source = "kong/konnect-beta" } time = { source = "hashicorp/time" } kubernetes = { source = "hashicorp/kubernetes" } helm = { source = "hashicorp/helm" } } } 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" } provider "helm" { kubernetes = { config_path = pathexpand(var.k8s_cluster_config_path) } } provider "kubernetes" { config_path = pathexpand(var.k8s_cluster_config_path) } EOF -
Download the new providers:
terraform init -upgrade
Create a system account
-
Create a
system-account.tffile that defines a system account and a token to authenticate the zone:cat <<'EOF' > system-account.tf 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 } EOF -
Store the token in Kubernetes by creating a
k8s.tffile:cat <<'EOF' > k8s.tf 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" } EOF
Create the zone
-
Create a
values.tftplfile with templated values for the zone, address, and control plane ID:cat <<'EOF' > values.tftpl 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 EOF -
Create a
zone.tffile that defines the zone:cat <<'EOF' > zone.tf resource "helm_release" "kong_mesh" { name = "kong-mesh" repository = "https://kong.github.io/kong-mesh-charts" chart = "kong-mesh" 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 })] } EOF -
Apply the changes:
terraform apply -auto-approveYou should see 7 resources created. The
helm_releasecan take some time to create:helm_release.kong_mesh: Creation complete after 54s [id=kong-mesh]
Validate
-
Confirm that the
MeshTrafficPermissionyou created earlier is now available in the zone:kubectl get meshtrafficpermissions.kuma.io -AYou should see:
NAMESPACE NAME kong-mesh-system allow-all-wd5xx76vc44b498c - In the Konnect sidebar, click Service Mesh.
- Click tf-cp. You should see the zone and the mesh you created.
Cleanup
Destroy the Terraform resources
From the ~/mesh-konnect working directory, remove all the resources created by this guide:
terraform destroyRemove the working directory
Return to the parent directory, then delete the working directory and the files created during this guide:
cd ../
rm -rf ~/mesh-konnectFAQs
What happens if I rename a Terraform resource?
Certain properties, such as the mesh name or policy name, are used as identifiers. Changing them results in a new resource being created and all dependent resources being recreated.
For example, changing the mesh name to another-name:
resource "konnect_mesh" "my_mesh" {
# ...
name = "another-name"
# ...
}forces replacement of both the konnect_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_traffic_permission.allow_all must be replaced
-/+ resource "konnect_mesh_traffic_permission" "allow_all" {
~ creation_time = "2025-03-13T09:53:00.606442Z" -> (known after apply)
~ mesh = "mesh1" -> "another-name" # forces replacement