Flush a public Dedicated Cloud Gateway managed cache using Terraform

TL;DR

Deploy a konnect_gateway_custom_plugin_streaming custom plugin that authenticates to the managed cache via AWS STS and runs FLUSHDB, exposed through a konnect_gateway_service and konnect_gateway_route, then trigger a flush by calling the Terraform output URL.

Prerequisites

This is a Konnect tutorial and requires a Konnect personal access token.

  1. Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.

  2. Export your token to an environment variable:

     export KONNECT_TOKEN='YOUR_KONNECT_PAT'
  3. Create an auth.tf file that configures the kong/konnect Terraform provider. Change server_url if you are using a region other than us:

    echo '
    terraform {
      required_providers {
        konnect = {
          source  = "kong/konnect"
        }
        konnect-beta = {
          source  = "kong/konnect-beta"
        }
      }
    }
       
    provider "konnect" {
      server_url = "https://us.api.konghq.com"
    }
       
    provider "konnect-beta" {
      server_url            = "https://us.api.konghq.com"
    }
    ' > auth.tf
  4. Next, initialize your project and download the provider:

    terraform init

The provider automatically uses the KONNECT_TOKEN environment variable if it is available. If you would like to use a custom authentication token, set the personal_access_token field alongside server_url in the provider block.

This tutorial requires an AWS managed cache attached to a ready public Dedicated Cloud Gateway control plane with a live data plane.

If you don’t have one configured yet, create one with Terraform.

You need to retrieve the provider account ID. First, make a GET request to the Konnect Cloud Gateways API using the /provider-accounts endpoint:

curl -X GET "https://global.api.konghq.com/v2/cloud-gateways/provider-accounts?filter%5Bprovider%5D%5Beq%5D=aws" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN"\
     -H "Accept: application/json"\
     -H "Content-Type: application/json"

Export the id from the output as a Terraform variable:

export TF_VAR_provider_id='YOUR_PROVIDER_ACCOUNT_ID'

Use the id from the output, not provider_account_id.

The supported region, availability zones, and CIDR blocks depend on your provider account. List the values that AWS supports from the availability endpoint:

curl -s -H "Authorization: Bearer $KONNECT_TOKEN" \
  https://global.api.konghq.com/v2/cloud-gateways/availability.json | \
  jq '.providers[] | select(.provider == "aws") | .regions[] | {region, availability_zones, cidr_blocks}'

Use a supported region, its availability_zones, and a CIDR subnet inside one of the supported cidr_blocks in the following configuration, which creates the public network, the control plane, a live data plane on that network, and the managed cache:

echo '
variable "provider_id" {
  type = string
}

resource "konnect_cloud_gateway_network" "my_cloudgatewaynetwork" {
  name   = "Terraform Network"
  region = "us-east-2"
  availability_zones = [
    "use2-az1",
    "use2-az2",
    "use2-az3"
  ]

  cidr_block      = "10.0.0.0/16"

  cloud_gateway_provider_account_id = var.provider_id
}

resource "konnect_gateway_control_plane" "test_cp" {
  name         = "CGW Control Plane"
  cloud_gateway = true
}

resource "konnect_cloud_gateway_configuration" "test_cp_configuration" {
  control_plane_id  = konnect_gateway_control_plane.test_cp.id
  control_plane_geo = "us"
  api_access        = "public"
  version           = "3.15"
  dataplane_groups = [
    {
      provider                  = "aws"
      region                    = "us-east-2"
      cloud_gateway_network_id  = konnect_cloud_gateway_network.my_cloudgatewaynetwork.id
      autoscale = {
        configuration_data_plane_group_autoscale_autopilot = {
          kind     = "autopilot"
          base_rps = 10
        }
      }
    }
  ]
}

resource "konnect_cloud_gateway_addon" "managed_cache" {
  name = "managed-cache"
  owner = {
    control_plane = {
      control_plane_id  = konnect_gateway_control_plane.test_cp.id
      control_plane_geo = "us"
    }
  }
  config = {
    managed_cache = {
      capacity_config = {
        tiered = {
          tier = "micro"
        }
      }
    }
  }
}

output "control_plane_id" {
  value = konnect_gateway_control_plane.test_cp.id
}
' >> main.tf

Create the resources using Terraform:

terraform apply -auto-approve

Important: It can take 30-40 minutes for your network to initialize, and about 15 minutes after that for the managed cache to become ready. You must wait for both the network and the managed cache to show as Ready before continuing.

For sizing recommendations or a full walkthrough, see Configure an AWS managed cache for a Dedicated Cloud Gateway control plane or Configure an AWS managed cache for a Dedicated Cloud Gateway control plane group.

This how-to is for Dedicated Cloud Gateways with a public network since it uses the control plane endpoint to flush the cache.

Upload the custom plugin

This how-to deploys a custom Kong plugin that exposes a flush endpoint on your gateway. Calling the endpoint authenticates to the managed cache with AWS STS-derived credentials and runs FLUSHDB, so you (or your CI/CD pipeline) can flush the cache without engineering involvement.

First, create and configure the custom plugin:

  1. Create a schema.lua file that defines the plugin’s configuration fields.

       echo '
       local typedefs = require "kong.db.schema.typedefs"
    
       return {
         name = "cache-flusher",
         fields = {
           { protocols = typedefs.protocols_http },
           {
             config = {
               type = "record",
               fields = {
               { host = typedefs.host({ default = "127.0.0.1", referenceable = true }) },
               { port = typedefs.port({ default = 6379, referenceable = true }) },
               { username = { type = "string", referenceable = true } },
               { ssl = { type = "boolean", default = true } },
               { server_name = { type = "string", referenceable = true } },
               { cloud_authentication = {
                   type = "record",
                   fields = {
                     { auth_provider = { type = "string", referenceable = true } },
                     { aws_region = { type = "string", referenceable = true } },
                     { aws_assume_role_arn = { type = "string", referenceable = true } },
                     { aws_cache_name = { type = "string", referenceable = true } },
                   }
               }},
             }
             },
           },
         },
       }
       ' > schema.lua

  2. Create a handler.lua file that authenticates to the managed cache and runs the flush.

       echo '
       local AWS = require "resty.aws"
       local redis = require "resty.redis"
       local aws_config = require "resty.aws.config"
    
       local AWS_DEFAULT_ROLE_SESSION_NAME = "KongElasticacheSession"
       local AWS_global_config = aws_config.global
       local aws = AWS({ region = AWS_global_config.region })
    
       local plugin = {
         PRIORITY = 980,
         VERSION  = "1.0.0",
       }
    
       local function get_aws_auth_token(conf, cloud_auth)
           local cachename = cloud_auth.aws_cache_name
           local name = conf.username
           local region = cloud_auth.aws_region
           local assume_role_arn = cloud_auth.aws_assume_role_arn
    
           local credentials = aws.config.credentials
    
           local sts, err = aws:STS({
               region = region,
               credentials = credentials,
               stsRegionalEndpoints = AWS_global_config.sts_regional_endpoints,
           })
           if not sts then
               return nil, err
           end
    
           local creds = aws:ChainableTemporaryCredentials {
               params = {
                   RoleArn = assume_role_arn,
                   RoleSessionName = AWS_DEFAULT_ROLE_SESSION_NAME,
               },
               sts = sts,
           }
    
           local cache = aws:ElastiCache({ region = region })
    
           local signer = cache:Signer {
               cachename = cachename,
               username = name,
               is_serverless = false,
               region = region,
               credentials = creds,
           }
    
           local auth_token, token_err = signer:getAuthToken()
           if token_err then
               return nil, token_err
           end
    
           return auth_token
       end
    
       local function get_auth_token(conf)
           local cloud_auth = conf.cloud_authentication
           local provider = cloud_auth.auth_provider
    
           if provider == "aws" then
               return get_aws_auth_token(conf, cloud_auth)
           end
    
           return nil, "cloud provider " .. tostring(provider) .. " not implemented"
       end
    
       function plugin:access(conf)
           kong.log.notice("[cache-flusher] starting cache flush")
    
           -- Connect to Cache
           kong.log.notice("[cache-flusher] connecting to Cache at ", conf.host, ":", conf.port)
    
           local red = redis:new()
           local ok, err = red:connect(conf.host, conf.port, {
               ssl = conf.ssl,
               ssl_verify = conf.ssl,
               server_name = conf.server_name,
           })
           if not ok then
               kong.log.err("[cache-flusher] failed to connect to Cache: ", err)
               return kong.response.error(500, "cache flush failed: Cache connect error")
           end
    
           kong.log.notice("[cache-flusher] connected to Cache, obtaining auth token")
    
           -- Authenticate with STS-derived token
           local auth_token, token_err = get_auth_token(conf)
           if not auth_token then
               kong.log.err("[cache-flusher] failed to obtain auth token: ", token_err)
               return kong.response.error(500, "cache flush failed: auth token error")
           end
    
           kong.log.notice("[cache-flusher] auth token obtained, authenticating with Cache")
    
           local res, auth_err = red:auth(conf.username, auth_token)
           if not res then
               kong.log.err("[cache-flusher] failed to authenticate with Cache: ", auth_err)
               return kong.response.error(500, "cache flush failed: Cache auth error")
           end
    
           kong.log.notice("[cache-flusher] authenticated, flushing Cache")
    
           -- Flush Cache
           local flush_res, flush_err = red:flushdb("ASYNC")
           if not flush_res then
               kong.log.err("[cache-flusher] failed to flush Cache: ", flush_err)
               return kong.response.error(500, "cache flush failed: Cache flushdb error")
           end
    
           kong.log.notice("[cache-flusher] cache flush completed successfully")
    
           return kong.response.exit(200, { message = "[cache-flusher] cache flushed" })
       end
    
       return plugin
       ' > handler.lua

  3. Declare the variables this how-to uses, and export the ones only you know:

     echo '
     variable "control_plane_id" {
       type = string
     }
    
     variable "flush_path" {
       type    = string
       default = "/konnect/managed-cache/flush"
     }
    
     variable "ip_allowlist" {
       type    = list(string)
       default = []
     }
    
     variable "auto_flush" {
       type    = bool
       default = false
     }
    
     variable "proxy_hostname" {
       type = string
     }
     ' > variables.tf

    If you created your control plane as part of the prereqs, get its ID directly from Terraform state:

     export TF_VAR_control_plane_id=$(terraform output -raw control_plane_id)

    Otherwise, export the ID of your existing control plane:

     export TF_VAR_control_plane_id="your-control-plane-id"
  4. Look up the Konnect proxy hostname for your control plane, and export it as a Terraform variable:

    CONTROL_PLANE_DETAILS=$(curl -X GET "https://us.api.konghq.com/v2/control-planes/$TF_VAR_control_plane_id" \
         --no-progress-meter --fail-with-body  \
         -H "Authorization: Bearer $KONNECT_TOKEN"
    )
  5. Export the proxy hostname:

    PROXY_HOSTNAME=$(echo $CONTROL_PLANE_DETAILS | jq -r '.config.control_plane_endpoint | sub("https://";"") | split(".")[0]')
    export TF_VAR_proxy_hostname="${PROXY_HOSTNAME}.gateways.konggateway.com"
  6. Define the custom plugin, the Gateway Service and Route that expose the flush endpoint, and the plugin instance:

     echo '
     resource "konnect_gateway_custom_plugin_streaming" "cache_flusher" {
       control_plane_id = var.control_plane_id
       name             = "cache-flusher"
       schema           = file("${path.module}/schema.lua")
       handler          = file("${path.module}/handler.lua")
     }
    
     resource "konnect_gateway_service" "cache_flusher_service" {
       control_plane_id = var.control_plane_id
       name             = "managed-cache-flush-service"
       host             = "127.0.0.1"
       port             = 443
       protocol         = "https"
     }
    
     resource "konnect_gateway_route" "cache_flusher_route" {
       control_plane_id = var.control_plane_id
       service = {
         id = konnect_gateway_service.cache_flusher_service.id
       }
       paths = [var.flush_path]
     }
    
     resource "konnect_gateway_custom_plugin" "cache_flusher_instance" {
       name             = "cache-flusher"
       control_plane_id = var.control_plane_id
       enabled          = true
       service = {
         id = konnect_gateway_service.cache_flusher_service.id
       }
       route = {
         id = konnect_gateway_route.cache_flusher_route.id
       }
       config = {
         "cloud_authentication" : {
           "auth_provider" : "{vault://env/ADDON_MANAGED_CACHE_AUTH_PROVIDER}",
           "aws_assume_role_arn" : "{vault://env/ADDON_MANAGED_CACHE_AWS_ASSUME_ROLE_ARN}",
           "aws_cache_name" : "{vault://env/ADDON_MANAGED_CACHE_AWS_CACHE_NAME}",
           "aws_region" : "{vault://env/ADDON_MANAGED_CACHE_AWS_REGION}"
         },
         "host" : "{vault://env/ADDON_MANAGED_CACHE_HOST}",
         "port" : "{vault://env/ADDON_MANAGED_CACHE_PORT}",
         "server_name" : "{vault://env/ADDON_MANAGED_CACHE_SERVER_NAME}",
         "ssl" : true,
         "username" : "{vault://env/ADDON_MANAGED_CACHE_USERNAME}"
       }
    
       depends_on = [konnect_gateway_custom_plugin_streaming.cache_flusher]
     }
     ' >> main.tf

    The {vault://env/ADDON_MANAGED_CACHE_*} references are populated automatically by Konnect once your managed cache add-on reaches a Ready state, so you don’t need to configure any AWS IAM credentials yourself.

  7. Add an output for the flush URL:

     echo '
     output "flush_proxy_url" {
       value = "https://${var.proxy_hostname}${var.flush_path}"
     }
     ' > output.tf

  8. Apply the configuration:

     terraform apply -auto-approve

    You’ll get a response like the following:

     Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Use the following sections to further configure the managed cache flush behavior.

Limit IPs that can trigger a flush

If you want to limit which IPs can trigger a flush, set ip_allowlist to a list of IPs or CIDRs, and add an ip-restriction plugin scoped to the flush route.

  1. Configure the IP Restriction plugin:

    echo '
    resource "konnect_gateway_plugin_ip_restriction" "ip-restriction-plugin" {
      count = length(var.ip_allowlist) > 0 ? 1 : 0
    
      config = {
        allow = var.ip_allowlist
    
        message = "NOT ALLOWED"
        status  = 405
      }
    
      control_plane_id = var.control_plane_id
      enabled          = true
      service = {
        id = konnect_gateway_service.cache_flusher_service.id
      }
      route = {
        id = konnect_gateway_route.cache_flusher_route.id
      }
    }
    ' >> main.tf
  2. Export the IPs you want to allow list:

    export TF_VAR_ip_allowlist='["203.0.113.0/24"]'
  3. Apply the Terraform configuration:

    terraform apply -auto-approve

Requests from IPs outside the allowlist receive a 405 response.

Automatically flush on terraform apply

  1. If you want the cache to flush automatically every time you run terraform apply, set auto_flush to true:

    echo '
    resource "terraform_data" "flush_cache" {
      count = var.auto_flush ? 1 : 0
    
      triggers_replace = [timestamp()]
    
      provisioner "local-exec" {
        command = "sleep 10 && curl -sf https://${var.proxy_hostname}${var.flush_path}"
      }
    
      depends_on = [
        konnect_gateway_route.cache_flusher_route,
        konnect_gateway_custom_plugin.cache_flusher_instance,
        konnect_gateway_plugin_ip_restriction.ip-restriction-plugin,
      ]
    }
    ' >> main.tf
  2. Export the auto flush behavior:

    export TF_VAR_auto_flush=true
  3. Apply the Terraform configuration:

    terraform apply -auto-approve

When auto_flush is true, every terraform apply triggers a cache flush, not just the first one. Subsequent applies also show the terraform_data.flush_cache resource being destroyed and recreated. This is expected and can be safely ignored.

Validate

Trigger a flush by calling the URL Terraform output in the previous step:

curl -sf "$(terraform output -raw flush_proxy_url)"

A successful flush returns:

{"message": "[cache-flusher] cache flushed"}

Cleanup

To remove the flush endpoint and all associated resources:

terraform destroy

FAQs

This how-to is scoped to Dedicated Cloud Gateways with a public network, because the flush endpoint is reached through the control plane endpoint. A private network Dedicated Cloud Gateway doesn’t have a control plane endpoint to route through.

Instead, reach the flush endpoint through the FQDN assigned to the private IP address of your Dedicated Cloud Gateway, for example through a CDN or nodes placed in front of the data planes, or over your internal/private network. As long as a client can reach that FQDN, the rest of this how-to (the custom plugin, Terraform resources, and flush call) applies the same way.

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!