Skip to main content

Azure Kubernetes Service on GKE Fleet β€” Lab Guide

πŸ“– Configuration Guide

This lab guide walks you through deploying an Azure Kubernetes Service (AKS) cluster and registering it as a GKE Attached Cluster in Google Cloud Fleet using the AKS_GKE module. You will then explore unified multi-cloud operations: accessing the AKS cluster via Google Cloud's Connect Gateway, centralised logging and monitoring through Google Cloud Observability, and fleet- wide access control β€” all without leaving Google Cloud.


Table of Contents​

  1. Overview
  2. Architecture
  3. Prerequisites
  4. Lab Setup
  5. Exercise 1 β€” Verify the Fleet Membership
  6. Exercise 2 β€” Access via Connect Gateway
  7. Exercise 3 β€” Deploy a Sample Workload
  8. Exercise 4 β€” Centralised Logging with Cloud Logging
  9. Exercise 5 β€” Managed Prometheus and Cloud Monitoring
  10. Exercise 6 β€” Fleet Access Control
  11. Exercise 7 β€” OIDC Federation and Connect Gateway API
  12. Exercise 8 β€” Platform Version Management
  13. Cleanup
  14. Reference

1. Overview​

What Is GKE Fleet?​

Google Cloud Fleet (formerly Anthos) provides a unified control plane for Kubernetes clusters across clouds and on-premises environments. By registering an Azure AKS cluster as a GKE Attached Cluster, you gain:

CapabilityWhat It Enables
Connect Gatewaykubectl access to AKS clusters via Google Cloud IAM β€” no VPN or bastion required
Cloud LoggingUnified Kubernetes system and workload logs from AKS in Cloud Logging
Managed PrometheusAKS cluster metrics collected and queryable in Cloud Monitoring
Fleet IAMSingle IAM model for access control across all fleet clusters
Multi-cloud visibilitySingle pane of glass for cluster health, nodes, and workloads

How GKE Attached Clusters Work​

GKE Attached Clusters use OIDC federation to establish trust between Azure AD (the AKS OIDC issuer) and Google Cloud. A lightweight GKE Connect Agent runs inside the AKS cluster and maintains an outbound connection to Google Cloud β€” no inbound firewall rules are required.

Azure Cloud                          Google Cloud
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ AKS Cluster β”‚ β”‚ GKE Fleet Hub β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ GKE Connect │◄─┼──outbound───►│ β”‚ Fleet Membership β”‚ β”‚
β”‚ β”‚ Agent β”‚ β”‚ HTTPS β”‚ β”‚ (OIDC trust established) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚
β”‚ β”‚ Cloud Logging β”‚ β”‚ β”‚ Connect Gateway API β”‚
β”‚ β”‚ DaemonSet β”‚ β”‚ β”‚ Cloud Logging β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Cloud Monitoring β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Architecture​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Azure (westus2) β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Resource Group: azure-aks-cluster-<id> β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ AKS Cluster β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β€’ Kubernetes 1.34 β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β€’ System-assigned managed identity β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β€’ OIDC issuer enabled β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β€’ Node pool: Standard_D2s_v3 (3 nodes default) β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ OIDC Federation + GKE Connect Agent (outbound HTTPS)
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Google Cloud (us-central1) β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ GKE Fleet Hub β”‚ β”‚
β”‚ β”‚ β€’ Fleet membership: azure-aks-cluster-<id> β”‚ β”‚
β”‚ β”‚ β€’ Platform version: 1.34.0-gke.1 β”‚ β”‚
β”‚ β”‚ β€’ Logging: SYSTEM + WORKLOADS β”‚ β”‚
β”‚ β”‚ β€’ Managed Prometheus enabled β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Cloud Logging β”‚ β”‚Cloud Monitoringβ”‚ β”‚ Connect Gateway API β”‚ β”‚
β”‚ β”‚ (AKS logs) β”‚ β”‚(AKS metrics) β”‚ β”‚ (kubectl access) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Module variable wiring:

AKS_GKE
client_id / client_secret /
tenant_id / subscription_id β†’ Azure Service Principal for AKS creation
node_count = 3 β†’ AKS default node pool size
vm_size = Standard_D2s_v3
k8s_version = "1.34"
trusted_users = ["user@example.com"] β†’ cluster-admin via Connect Gateway

3. Prerequisites​

Required Tools​

ToolMinimum VersionInstall
gcloud CLI480.0.0Install guide
kubectl1.29+gcloud components install kubectl
gke-gcloud-auth-pluginAnygcloud components install gke-gcloud-auth-plugin
az CLIAnyAzure CLI install
curl / jqAnySystem package manager

Azure Requirements​

You need an Azure Service Principal with at least Contributor rights on the target subscription. Collect these four values before deploying:

  • Client ID (client_id) β€” Azure AD Application (client) ID
  • Client Secret (client_secret) β€” Azure AD client secret value
  • Tenant ID (tenant_id) β€” Azure AD Directory (tenant) ID
  • Subscription ID (subscription_id) β€” Azure subscription ID

GCP Permissions​

roles/container.admin
roles/gkehub.admin
roles/iam.serviceAccountAdmin
roles/logging.admin
roles/monitoring.admin

Environment Variables​

export PROJECT_ID="your-gcp-project-id"
export GCP_REGION="us-central1"
export CLUSTER_NAME="azure-aks-cluster" # adjust if deployment_id was set

gcloud config set project "${PROJECT_ID}"
gcloud config set compute/region "${GCP_REGION}"

4. Lab Setup​

4.1 Deploy via RAD UI​

Deploy the AKS_GKE module via the RAD UI. In the variable form, set the following key variables:

VariableValueNotes
project_idyour-gcp-project-idRequired
gcp_locationus-central1GCP region for Fleet membership
azure_regionwestus2Azure region for AKS cluster
client_id<your-app-client-id>Azure Service Principal App ID
client_secret<your-app-secret>Azure Service Principal secret
tenant_id<your-tenant-id>Azure AD Tenant ID
subscription_id<your-subscription-id>Azure Subscription ID
node_count3Default AKS node count
k8s_version1.34Kubernetes version
trusted_users["your-email@example.com"]Users granted cluster-admin

Click Deploy and wait for provisioning to complete (approximately 15–20 minutes).

What this provisions: An Azure Resource Group, AKS cluster with OIDC issuer enabled, GKE Attached Cluster registration in Fleet Hub with OIDC trust, Cloud Logging for system and workload logs, and Managed Prometheus for metrics collection.

4.2 Configure Azure CLI (Optional)​

az login --service-principal \
--username "${AZURE_CLIENT_ID}" \
--password "${AZURE_CLIENT_SECRET}" \
--tenant "${AZURE_TENANT_ID}"

az aks list --subscription "${AZURE_SUBSCRIPTION_ID}" \
--output table

Exercise 1 β€” Verify the Fleet Membership​

Objective​

Confirm that the AKS cluster is correctly registered in Google Cloud Fleet and all managed components are healthy.

Step 1.1 β€” List Fleet Memberships​

gcloud:

gcloud container fleet memberships list --project="${PROJECT_ID}"

Expected output:

NAME                              EXTERNAL_ID                            LOCATION
azure-aks-cluster-<id> xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx global

REST API:

curl -s \
"https://gkehub.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/memberships" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
| jq '.resources[] | {name, state: .state.code}'

Step 1.2 β€” Inspect Membership Details​

gcloud:

gcloud container fleet memberships describe "${CLUSTER_NAME}" \
--project="${PROJECT_ID}"

Look for:

  • state.code: READY β€” membership is active
  • endpoint.kubernetesMetadata.kubernetesApiServerVersion β€” Kubernetes version
  • authority.issuer β€” OIDC issuer URL from AKS

REST API:

curl -s \
"https://gkehub.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/memberships/${CLUSTER_NAME}" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
| jq '{name, state: .state.code, k8sVersion: .endpoint.kubernetesMetadata.kubernetesApiServerVersion}'

Step 1.3 β€” View in Google Cloud Console​

Navigate to: Kubernetes Engine β†’ Clusters β†’ look for the Azure cluster (shown with an Azure icon)

Or directly:

echo "https://console.cloud.google.com/kubernetes/list/overview?project=${PROJECT_ID}"

Step 1.4 β€” Verify GKE Connect Agent​

# Configure kubectl (done in Exercise 2 Step 2.1 below)
# Then verify the Connect agent namespace:
kubectl get pods -n gke-connect

Expected:

NAME                             READY   STATUS    RESTARTS
gke-connect-agent-xxxxxxx 1/1 Running 0

Exercise 2 β€” Access via Connect Gateway​

Objective​

Use Google Cloud's Connect Gateway to access the AKS cluster with kubectl using your Google Cloud IAM identity β€” without needing Azure credentials, a VPN, or direct network access.

Step 2.1 β€” Configure kubectl via Connect Gateway​

gcloud container fleet memberships get-credentials "${CLUSTER_NAME}" \
--project="${PROJECT_ID}"

# Verify the context was added
kubectl config get-contexts
kubectl config current-context

Step 2.2 β€” Verify Cluster Connectivity​

kubectl cluster-info

# Expected:
# Kubernetes control plane is running at https://connectgateway.googleapis.com/...

kubectl get nodes -o wide

Expected node output:

NAME                             STATUS   ROLES    AGE   VERSION
aks-nodepool1-xxxxxxxx-vmss0 Ready agent 10m v1.34.x
aks-nodepool1-xxxxxxxx-vmss1 Ready agent 10m v1.34.x
aks-nodepool1-xxxxxxxx-vmss2 Ready agent 10m v1.34.x

Step 2.3 β€” Inspect Cluster Namespaces​

kubectl get namespaces

# Standard AKS namespaces:
# default
# kube-system
# kube-public
# kube-node-lease
# gke-connect ← GKE Connect Agent

Step 2.4 β€” Verify Admin Access​

# Verify the trusted_users entry grants cluster-admin
kubectl auth can-i list pods --all-namespaces
# Expected: yes

kubectl auth can-i create clusterrolebindings
# Expected: yes

Step 2.5 β€” Inspect the GKE Connect Agent Pod​

kubectl describe pod -n gke-connect -l app=gke-connect-agent

# Key information:
# - Image version (platform version)
# - Environment variables (project number, membership name)
# - Resource limits

Exercise 3 β€” Deploy a Sample Workload​

Objective​

Deploy an nginx application to the AKS cluster via Connect Gateway and verify it appears in Cloud Logging and Cloud Monitoring.

Step 3.1 β€” Create a Namespace​

kubectl create namespace sample-workload
kubectl label namespace sample-workload app=sample

Step 3.2 β€” Deploy nginx​

# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: sample-workload
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: sample-workload
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
type: LoadBalancer
kubectl apply -f nginx-deployment.yaml

# Wait for pods to be ready
kubectl get pods -n sample-workload -w

Step 3.3 β€” Get the Service External IP​

kubectl get service nginx -n sample-workload -w

# Wait for EXTERNAL-IP to be assigned (Azure load balancer provisioning takes ~2 minutes)
NGINX_IP=$(kubectl get service nginx -n sample-workload \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "Nginx IP: ${NGINX_IP}"

# Test the endpoint
curl -s "http://${NGINX_IP}" | grep "<title>"
# Expected: <title>Welcome to nginx!</title>

Step 3.4 β€” Verify Pod Distribution​

# Check which nodes the pods landed on
kubectl get pods -n sample-workload -o wide

# Check pod resource usage
kubectl top pods -n sample-workload
kubectl top nodes

Step 3.5 β€” Generate Traffic for Logs​

for i in $(seq 1 50); do
curl -s -o /dev/null "http://${NGINX_IP}"
sleep 0.5
done

Exercise 4 β€” Centralised Logging with Cloud Logging​

Objective​

Explore Kubernetes system and workload logs from the AKS cluster collected automatically by Cloud Logging.

Step 4.1 β€” View Logs in Logs Explorer​

Navigate to:

echo "https://console.cloud.google.com/logs/query?project=${PROJECT_ID}"

Step 4.2 β€” Query System Component Logs​

gcloud:

gcloud logging read \
"resource.type=k8s_cluster AND resource.labels.cluster_name=${CLUSTER_NAME}" \
--project="${PROJECT_ID}" \
--limit=20 \
--format=json \
| jq '.[] | {timestamp, message: .textPayload}'

REST API:

curl -s -X POST \
"https://logging.googleapis.com/v2/entries:list" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d "{
\"resourceNames\": [\"projects/${PROJECT_ID}\"],
\"filter\": \"resource.type=k8s_cluster resource.labels.cluster_name=${CLUSTER_NAME}\",
\"pageSize\": 10
}" | jq '.entries[] | {timestamp, message: .textPayload}'

Step 4.3 β€” Query Workload Logs (nginx)​

gcloud:

gcloud logging read \
"resource.type=k8s_container \
AND resource.labels.namespace_name=sample-workload \
AND resource.labels.container_name=nginx" \
--project="${PROJECT_ID}" \
--limit=20 \
--format=json \
| jq '.[] | {timestamp, httpRequest: .httpRequest}'

REST API:

curl -s -X POST \
"https://logging.googleapis.com/v2/entries:list" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d "{
\"resourceNames\": [\"projects/${PROJECT_ID}\"],
\"filter\": \"resource.type=k8s_container resource.labels.namespace_name=sample-workload\",
\"pageSize\": 10
}" | jq '.entries[].jsonPayload'

Step 4.4 β€” Log-Based Metrics​

Create a log-based metric to count nginx requests:

gcloud:

gcloud logging metrics create nginx-request-count \
--description="Count of nginx requests from AKS cluster" \
--log-filter="resource.type=k8s_container \
AND resource.labels.namespace_name=sample-workload \
AND resource.labels.container_name=nginx" \
--project="${PROJECT_ID}"

REST API:

curl -s -X POST \
"https://logging.googleapis.com/v2/projects/${PROJECT_ID}/metrics" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d '{
"name": "nginx-request-count",
"description": "Count of nginx requests from AKS cluster",
"filter": "resource.type=k8s_container AND resource.labels.namespace_name=sample-workload"
}'

Exercise 5 β€” Managed Prometheus and Cloud Monitoring​

Objective​

Explore Kubernetes metrics from the AKS cluster collected by Managed Prometheus and visualised in Cloud Monitoring.

Step 5.1 β€” Open the Kubernetes Engine Dashboard​

echo "https://console.cloud.google.com/monitoring/dashboards?project=${PROJECT_ID}"

Navigate to Dashboards β†’ Kubernetes Engine β†’ select the AKS cluster.

Step 5.2 β€” Query Metrics via Cloud Monitoring API​

gcloud:

gcloud monitoring metrics list \
--filter="metric.type:kubernetes" \
--project="${PROJECT_ID}" \
| grep -E "container/cpu|container/memory|node/cpu"

REST API (MQL query β€” CPU utilisation per node):

curl -s -X POST \
"https://monitoring.googleapis.com/v3/projects/${PROJECT_ID}/timeSeries:query" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d '{
"query": "fetch k8s_node::kubernetes.io/node/cpu/allocatable_utilization | within 1h | group_by [resource.cluster_name], mean(val())"
}' | jq '.timeSeriesData[].pointData[-1].values'

Step 5.3 β€” Node and Pod Resource Usage​

# Current node resource consumption
kubectl top nodes

# Current pod resource consumption
kubectl top pods -n sample-workload

# All namespaces
kubectl top pods --all-namespaces | sort -k3 -rn | head -20

Step 5.4 β€” Create an Alerting Policy​

gcloud (alert when CPU > 80%):

gcloud alpha monitoring policies create \
--notification-channels="" \
--display-name="AKS High CPU" \
--condition-filter="metric.type=\"kubernetes.io/node/cpu/allocatable_utilization\" resource.type=\"k8s_node\" resource.label.\"cluster_name\"=\"${CLUSTER_NAME}\"" \
--condition-threshold-value=0.8 \
--condition-threshold-duration=300s \
--condition-threshold-comparison=COMPARISON_GT \
--project="${PROJECT_ID}"

Exercise 6 β€” Fleet Access Control​

Objective​

Understand the two-layer authorisation model for Connect Gateway access and grant a colleague access to the AKS cluster using Google Cloud IAM and Kubernetes RBAC.

Background: Two-Layer Authorisation​

User
β”‚
β–Ό
Google Cloud IAM
(roles/gkehub.gatewayReader or roles/gkehub.gatewayEditor)
β”‚ Allows: traverse Connect Gateway
β–Ό
Kubernetes RBAC
(ClusterRoleBinding with Google identity)
β”‚ Allows: specific Kubernetes API actions
β–Ό
AKS Cluster API Server

Step 6.1 β€” View Current RBAC Bindings​

kubectl get clusterrolebindings \
| grep -v system

kubectl describe clusterrolebinding \
"$(kubectl get clusterrolebindings -o name | grep -i google | head -1)"

Step 6.2 β€” Grant a Colleague Read-Only Access​

Replace colleague@example.com with the actual Google identity:

# Step 1: Grant IAM permission to use Connect Gateway
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
--member="user:colleague@example.com" \
--role="roles/gkehub.gatewayReader"

# Step 2: Create a Kubernetes RBAC binding for cluster-level read
kubectl create clusterrolebinding colleague-view \
--clusterrole=view \
--user="colleague@example.com"

REST API (IAM binding):

curl -s -X POST \
"https://cloudresourcemanager.googleapis.com/v1/projects/${PROJECT_ID}:setIamPolicy" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d '{
"policy": {
"bindings": [{
"role": "roles/gkehub.gatewayReader",
"members": ["user:colleague@example.com"]
}]
}
}'

Step 6.3 β€” Verify Your Own Admin Permissions​

kubectl auth can-i list pods --all-namespaces
kubectl auth can-i create deployments -n sample-workload
kubectl auth can-i delete namespaces

Step 6.4 β€” Review Audit Logs​

gcloud logging read \
"protoPayload.serviceName=connectgateway.googleapis.com" \
--project="${PROJECT_ID}" \
--limit=10 \
--format=json \
| jq '.[] | {
timestamp,
caller: .protoPayload.authenticationInfo.principalEmail,
method: .protoPayload.methodName
}'

Exercise 7 β€” OIDC Federation and Connect Gateway API​

Objective​

Understand how OIDC federation enables Connect Gateway to authenticate Google identities to the AKS API server, and make direct Connect Gateway API calls.

Step 7.1 β€” Inspect the OIDC Trust Configuration​

gcloud container fleet memberships describe "${CLUSTER_NAME}" \
--project="${PROJECT_ID}" \
--format="yaml(authority)"

Expected:

authority:
issuer: https://oidc.prod-aks.azure.com/<tenant-id>/<cluster-id>/
workloadIdentityPool: <project-id>.hub.id.goog
identityProvider: https://gkehub.googleapis.com/projects/...

REST API:

curl -s \
"https://gkehub.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/memberships/${CLUSTER_NAME}" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
| jq '.authority'

Step 7.2 β€” Direct Connect Gateway API Call​

Connect Gateway exposes a Kubernetes-compatible API at a Google-hosted endpoint:

# Get the Connect Gateway endpoint
GATEWAY_URL=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
echo "Connect Gateway URL: ${GATEWAY_URL}"

# Make a direct API call with your Google auth token
ACCESS_TOKEN=$(gcloud auth print-access-token)

curl -s \
"${GATEWAY_URL}/api/v1/namespaces" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
| jq '.items[].metadata.name'

Step 7.3 β€” Verify the GKE Connect Agent Version​

kubectl get pod -n gke-connect -o yaml \
| grep "image:" | grep -v "imagePullPolicy"

The image tag corresponds to the platform_version variable (e.g., 1.34.0-gke.1).


Exercise 8 β€” Platform Version Management​

Objective​

Understand how GKE Attached Cluster platform versions work and how to upgrade the Connect Agent when a new version is available.

Step 8.1 β€” List Available Platform Versions​

gcloud:

gcloud container attached get-server-config \
--location="${GCP_REGION}" \
--project="${PROJECT_ID}"

This returns supported Kubernetes version ranges and the latest Connect Agent platform version for each.

REST API:

curl -s \
"https://gkemulticloud.googleapis.com/v1/projects/${PROJECT_ID}/locations/${GCP_REGION}/attachedServerConfig" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
| jq '.validVersions[] | {kubernetesVersion, platformVersion}'

Step 8.2 β€” Check Current Platform Version​

gcloud container fleet memberships describe "${CLUSTER_NAME}" \
--project="${PROJECT_ID}" \
--format="value(endpoint.kubernetesMetadata.kubernetesApiServerVersion)"

Step 8.3 β€” Upgrade the Platform Version (via RAD UI)​

To upgrade the Connect Agent to a newer platform version:

  1. Return to the RAD UI and navigate to your AKS_GKE deployment.
  2. Update the platform_version variable to the new version (e.g., 1.34.1-gke.1).
  3. Click Update.

The Terraform run updates only the attached cluster resource β€” the AKS cluster itself is not affected.


13. Cleanup​

When you are finished, return to the RAD UI and click Undeploy on the AKS_GKE deployment. This removes:

  • The GKE Fleet membership
  • The Azure AKS cluster
  • The Azure Resource Group and all contained resources

Manual Cleanup (if needed)​

gcloud β€” remove Fleet membership:

gcloud container fleet memberships delete "${CLUSTER_NAME}" \
--project="${PROJECT_ID}" \
--quiet

REST API β€” delete Fleet membership:

curl -s -X DELETE \
"https://gkehub.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/memberships/${CLUSTER_NAME}" \
-H "Authorization: Bearer $(gcloud auth print-access-token)"

az β€” delete Azure resources:

az group delete \
--name "azure-aks-cluster-<deployment-id>" \
--subscription "${AZURE_SUBSCRIPTION_ID}" \
--yes --no-wait

Clean up kubectl context:

kubectl config delete-context \
"connectgateway_${PROJECT_ID}_global_${CLUSTER_NAME}"

14. Reference​

Key Module Variables​

VariableTypeDefaultDescription
project_idstringβ€”GCP project ID (required)
gcp_locationstringus-central1GCP region for Fleet membership
azure_regionstringwestus2Azure region for AKS cluster
cluster_name_prefixstringazure-aks-clusterResource name prefix
k8s_versionstring1.34Kubernetes version for AKS
platform_versionstring1.34.0-gke.1GKE Connect Agent platform version
node_countnumber3AKS default node pool size
vm_sizestringStandard_D2s_v3Azure VM SKU for AKS nodes
trusted_userslist(string)[]Google identities granted cluster-admin
client_idstringβ€”Azure Service Principal App ID (required)
client_secretstringβ€”Azure Service Principal secret (required)
tenant_idstringβ€”Azure AD Tenant ID (required)
subscription_idstringβ€”Azure Subscription ID (required)

IAM Roles Required for Connect Gateway Access​

RolePurpose
roles/gkehub.gatewayReaderRead-only kubectl access via Connect Gateway
roles/gkehub.gatewayEditorRead-write kubectl access via Connect Gateway
roles/gkehub.gatewayAdminFull kubectl access via Connect Gateway
roles/gkehub.viewerView Fleet membership details

GCP APIs Enabled by the Module​

APIPurpose
gkemulticloud.googleapis.comGKE Attached Clusters management
gkeconnect.googleapis.comConnect Agent
connectgateway.googleapis.comConnect Gateway kubectl proxy
anthos.googleapis.comAnthos/Fleet platform
logging.googleapis.comCloud Logging
monitoring.googleapis.comCloud Monitoring
gkehub.googleapis.comFleet Hub
opsconfigmonitoring.googleapis.comManaged Prometheus
kubernetesmetadata.googleapis.comKubernetes metadata collection

Useful Commands Reference​

# List fleet memberships
gcloud container fleet memberships list --project="${PROJECT_ID}"

# Configure kubectl via Connect Gateway
gcloud container fleet memberships get-credentials <cluster-name> --project="${PROJECT_ID}"

# Describe membership details
gcloud container fleet memberships describe <cluster-name> --project="${PROJECT_ID}"

# List available attached cluster versions
gcloud container attached get-server-config --location="${GCP_REGION}" --project="${PROJECT_ID}"

# View cluster audit logs
gcloud logging read "protoPayload.serviceName=connectgateway.googleapis.com" --project="${PROJECT_ID}"

# Check node resource usage (via Connect Gateway)
kubectl top nodes

# View all namespaces
kubectl get namespaces

# Verify RBAC permissions
kubectl auth can-i list pods --all-namespaces

Further Reading​