Table of Contents
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 akube_admin_configavailable, 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.
- Tail from the deployment:
kubectl logs deploy/gateway-approuting-istio --tail=20 -f - 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:
