aksgatewayapi

AKS With Public Gateway API

Summary: This wiki page shows how I use terraform to setup a public AKS cluster, and install and configure API gateway.
Date: 24 August 2026

Last year the kubernetes community announced the deprecation of ingress NGINX. This was one of the most used ways to configure ingress on a kubernetes cluster, so this caused quite some disturbance. At the time I was working for a customer with AKS, so come in, Microsoft support. They quickly announced that no immediate action was required and to wait for them to come up with a new solution for aks ingress. And indeed in March of this year, they announced the Gateway API as the way forward with in June the General availability and a demo. For my customer I created a way forward as I was leaving them by then, and testing the Gateway API for them with a private AKS cluster.

However, for my self, I've always wanted a quick setup that I could use for testing services, updates and more. Using a private cluster for that is of course possible, but a bit of a hassle as you'd need a way into the network, for example with an additional VM and a bastion. So I decided that the Gateway API would be a perfect excuse to setup a public AKS cluster, configure the Gateway API and get just a bit more comfortable with terraform.

So, that's exactly what I did. To summarize, I created a terraform app that deploys a public AKS cluster with a Gateway API. The only downside is that the installation is a two-step procedure. Luckily, after the first step terraform outputs the exact commando to move on. On top of all that, the tls certificate for the API gateway as well as the kube_config are automatically created, so you can use the cluster afterwards right away.

The Terraform App

The terraform app is available from my public Github. Just download the files, and make sure to either update the variables or use a Terraform Override File file. In that case, I would suggest to setup these overrides:

provider "azurerm" {
  subscription_id = "Your Azure subscription"
  client_id       = "The client ID to access Azure"
  client_secret   = "The password for the client id above"
  tenant_id       = "Your Entra ID Tenant"
  features {}
}
 
variable "aks_admins" {
  default = "The object id of the group that holds your admin accounts"
}
 
variable "allowed_cidrs" {
  description = "CIDR ranges allowed to access resources."
  default     = ["1.1.1.1/32", "2.2.2.2/32"]
}
 
variable "kube_config_path" {
  default = "C:/the/path/for/your_kube_config_file"
}
 
variable "tls_cert_path" {
  default = "C:/the/path/for/your_gateway_certificate_file"
}

Once the variables are in place, login to azure (az login) and simply run terraform init, terraform plan and terraform apply to start the deployment. As said above, terraform will show you the command for the second step, which is terraform apply -var=deploy_k8s_resources=true -auto-approve. Finally, at the end, you'll be shown a last instruction. You need to update the local hosts file. To do so, open your hosts file C:\Windows\System32\drivers\etc\hosts as an administrator and add the public IP address of the Gateway and the public domain address you're using. Now the installation is done. Note that sometimes it takes a while for the gateway to load, which results in an error instead of the correct instructions. In case this happens, wait for a minute and run the terraform apply -var=deploy_k8s_resources=true -auto-approve command again.

Note: The terraform app is creating it's own certificate for the gateway. In case you want to use your own signed certificate, remove these steps and add your own certificate to the keyvault. As a final step, instead of updating your local hosts file, you need to create a record in your DNS zone towards the public gateway IP address.

Use Kubectl to Access K8S

Terraform will create a kubeconfig file for you, of which you can configure the location:

variable "kube_config_path" {
  description = "(Optional) Specifies the path to save the kubeconfig file"
  type        = string
  default     = "C:/Users/aks/.kube/aks_public_user.yaml"
}
Note: If required you could create the kube config file manually in powershell like this: terraform output -raw kube_user_config | Set-Content -Path "$HOME\.kube\aks_public_user.yaml" and in bash like this: terraform output -raw kube_user_config > ~/.kube/aks_public_user.yaml

When for example using lens, and you've added the kube config file to the default directory $HOME\.kube, the new kubeconfig will be added automatically to the list of clusters. When trying to access, Lens will provide with a URL and device code. After logging in, you can use Lens to access kubernetes, and check your credentials and permissions:

# Check your credentials
kubectl auth whoami
ATTRIBUTE    VALUE
Username     sjoerd@getshifting.com
Groups       [6cc24144-a32a-47fb-bfc7-6e32b9245487 86444dda-1f7e-482f-be82-d1e9d04ec68d system:authenticated]
Extra: oid   [c7e04739-b191-46c5-ba37-d12a0aea2439]
 
# Check your permissions
kubectl auth can-i "*" "*"
yes
 
# Check the cluster role binding that is automatically created for the admin group
kubectl describe clusterRoleBinding aks-cluster-admin-binding-aad
Name:         aks-cluster-admin-binding-aad
Labels:       addonmanager.kubernetes.io/mode=Reconcile
              kubernetes.io/cluster-service=true
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  cluster-admin
Subjects:
  Kind   Name                                  Namespace
  ----   ----                                  ---------
  Group  86444dda-1f7e-482f-be82-d1e9d04ec68d
Note: There is also a kube_admin_config available, which is using the local admin option. This is used by the kubernetes provider inside the terraform app. Note that the admin login is considered a backdoor, is not logged and should not be used in production, or any serious environment.

Test the Gateway

Besides the AKS cluster itself and the API Gateway, I've also added the httpbin test service, so you can verify the whole setup. I've modified the standard setup to work with a workload identity and get the certificate from a keyvault, which is more realistic scenario then just creating the certificate as a kubernetes secret and getting it from there.

Note that according to this blog you might also see the nginx pods getting loaded by default, until the default deployment gets stopped in November 2026.

Validate the SecretProviderClass

If everything works correctly, you'll see the secretproviderclass as well as the secret that gets created. Inside the secret will be the certificate from the keyvault which is also created by the terraform deployment:

kubectl get secretproviderclass,secret -n ops
NAME                                                     AGE
secretproviderclass.secrets-store.csi.x-k8s.io/httpbin   2m27s
 
NAME                        TYPE                DATA   AGE
secret/httpbin-credential   kubernetes.io/tls   2      2m25s

Validate the Gateway Resources

Wait for gateway to be programmed:

kubectl wait --for=condition=programmed gateways.gateway.networking.k8s.io gateway -n ops
gateway.gateway.networking.k8s.io/gateway condition met
 
kubectl get gateways -n ops
NAME      CLASS              ADDRESS         PROGRAMMED   AGE
gateway   approuting-istio   108.141.74.99   True         4m26s

Update local Hosts file

To be able to validate the setup, you need to make sure your local laptop can resolve the domain name you configured to the public IP address the gateway is using. After the second deployment, terraform will output the IP address of the gateway as well as the domain name you configured. Make sure to open the hosts file C:\Windows\System32\drivers\etc\hosts as administrator and add the correct line:

# public aks - aks.getshifting.com
108.141.74.99 aks.getshifting.com

Do the Teapot test

To do the teapot test, we also need to have the certificate that was created locally, which is something terraform manages for us as well:

variable "tls_cert_path" {
  description = "(Optional) Specifies the path to save the TLS certificate file"
  type        = string
  default     = "C:/Users/aks/.kube/aks_public_tls.crt"
}

To do the teapot test, do a curl to the 418 status: curl -v -H "Host: aks.getshifting.com" --cacert "C:/Users/aks/.kube/aks_public_tls.crt" "https://aks.getshifting.com/status/418"

PS C:\...\apps\azureAks[main]> curl -v -H "Host: aks.getshifting.com" --cacert "C:/Users/aks/.kube/aks_public_tls.crt"  "https://aks.getshifting.com/status/418"
* Host aks.getshifting.com:443 was resolved.
* IPv6: (none)
* IPv4: 108.141.74.99
*   Trying 108.141.74.99:443...
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* schannel: added 1 certificate(s) from CA file 'C:/Users/sjoer/.kube/aks_public_tls.crt'
* schannel: connection hostname (aks.getshifting.com) validated against certificate name (*.getshifting.com)
* ALPN: server accepted http/1.1
* Established connection to aks.getshifting.com (108.141.74.99 port 443) from 192.168.178.242 port 52417
* using HTTP/1.x
> GET /status/418 HTTP/1.1
> Host: aks.getshifting.com
> User-Agent: curl/8.21.0
> Accept: */*
>
* Request completely sent off
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: reusing certificate store from cache
* schannel: connection hostname (aks.getshifting.com) validated against certificate name (*.getshifting.com)
* schannel: SSL/TLS connection renegotiated
< HTTP/1.1 418 Unknown
< access-control-allow-credentials: true
< access-control-allow-origin: *
< content-type: text/plain; charset=utf-8
< x-more-info: http://tools.ietf.org/html/rfc2324
< date: Thu, 13 Aug 2026 19:38:52 GMT
< content-length: 13
< x-envoy-upstream-service-time: 0
< server: istio-envoy
<
I'm a teapot!* Connection #0 to host aks.getshifting.com:443 left intact

Alternatively, you could use a browser, as long as it's configured to accept the unsecure connection. Simply go to https://aks.getshifting.com/status/418 and you'll be presented with a “I'm a teapot!” page.

Check Access logs on the gateway proxy

Each Gateway resource is backed by a managed Deployment named <gateway-name>-approuting-istio:

kubectl get deployment gateway-approuting-istio -n ops
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
gateway-approuting-istio   2/2     2            2           23m

You can tail the logs from the deployment, but as there are two pods, you might get to see the logs from the wrong pod. You can also tail the logs from all pods using a label.

  1. Tail from the deployment: kubectl logs deploy/gateway-approuting-istio --tail=20 -f
  2. Tail from both pods using a label: kubectl logs -l gateway.networking.k8s.io/gateway-class-name=approuting-istio -n ops --tail=40 -f

Terraform Destroy

Afterwards you can clean it all up using the following command:

terraform destroy -var=deploy_k8s_resources=true -auto-approve

The only think left to do is to cleanup the hosts file.

This wiki has been made possible by:

aksgatewayapi.txt · Last modified: by 127.0.0.1