I've followed the Microsoft article and created a Kubernetes cluster in Azure. Below is the complete terraform code that I have used to built the Kubernetes cluster along with the AppGateway.
variable "resource_group_name_prefix" {
default = "rg"
description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription."
}
variable "resource_group_location" {
default = "eastus"
description = "Location of the resource group."
}
variable "virtual_network_name" {
description = "Virtual network name"
default = "aksVirtualNetwork"
}
variable "virtual_network_address_prefix" {
description = "VNET address prefix"
default = "192.168.0.0/16"
}
variable "aks_subnet_name" {
description = "Subnet Name."
default = "kubesubnet"
}
variable "aks_subnet_address_prefix" {
description = "Subnet address prefix."
default = "192.168.0.0/24"
}
variable "app_gateway_subnet_address_prefix" {
description = "Subnet server IP address."
default = "192.168.1.0/24"
}
variable "app_gateway_name" {
description = "Name of the Application Gateway"
default = "ApplicationGateway1"
}
variable "app_gateway_sku" {
description = "Name of the Application Gateway SKU"
default = "Standard_v2"
}
variable "app_gateway_tier" {
description = "Tier of the Application Gateway tier"
default = "Standard_v2"
}
variable "aks_name" {
description = "AKS cluster name"
default = "aks-cluster1"
}
variable "aks_dns_prefix" {
description = "Optional DNS prefix to use with hosted Kubernetes API server FQDN."
default = "aks"
}
variable "aks_agent_os_disk_size" {
description = "Disk size (in GB) to provision for each of the agent pool nodes. This value ranges from 0 to 1023. Specifying 0 applies the default disk size for that agentVMSize."
default = 40
}
variable "aks_agent_count" {
description = "The number of agent nodes for the cluster."
default = 1
}
variable "aks_agent_vm_size" {
description = "VM size"
default = "Standard_B8ms"
}
variable "aks_service_cidr" {
description = "CIDR notation IP range from which to assign service cluster IPs"
default = "10.0.0.0/16"
}
variable "aks_dns_service_ip" {
description = "DNS server IP address"
default = "10.0.0.10"
}
variable "aks_docker_bridge_cidr" {
description = "CIDR notation IP for Docker bridge."
default = "172.17.0.1/16"
}
variable "aks_enable_rbac" {
description = "Enable RBAC on the AKS cluster. Defaults to false."
default = "false"
}
variable "vm_user_name" {
description = "User name for the VM"
default = "vmuser1"
}
variable "public_ssh_key_path" {
description = "Public key path for SSH."
default = "./keys/id_rsa.pub"
}
variable "tags" {
type = map(string)
default = {
source = "terraform"
}
}
# Locals block for hardcoded names
locals {
backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap"
frontend_port_name = "${azurerm_virtual_network.test.name}-feport"
frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip"
http_setting_name = "${azurerm_virtual_network.test.name}-be-htst"
listener_name = "${azurerm_virtual_network.test.name}-httplstn"
request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt"
app_gateway_subnet_name = "appgwsubnet"
subscription_id = "<subscription_id>"
tenant_id = "<tenant_id>"
client_id = "<client_id>"
client_secret = "<client_secret>"
client_objectid = "<client_objectid>"
}
terraform {
required_version = ">=0.12"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>2.0"
}
}
}
provider "azurerm" {
subscription_id = local.subscription_id
tenant_id = local.tenant_id
client_id = local.client_id
client_secret = local.client_secret
features {}
}
resource "random_pet" "rg-name" {
prefix = var.resource_group_name_prefix
}
resource "azurerm_resource_group" "rg" {
name = random_pet.rg-name.id
location = var.resource_group_location
}
# User Assigned Identities
resource "azurerm_user_assigned_identity" "testIdentity" {
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
name = "identity1"
tags = var.tags
}
resource "azurerm_virtual_network" "test" {
name = var.virtual_network_name
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
address_space = [var.virtual_network_address_prefix]
subnet {
name = var.aks_subnet_name
address_prefix = var.aks_subnet_address_prefix
}
subnet {
name = "appgwsubnet"
address_prefix = var.app_gateway_subnet_address_prefix
}
tags = var.tags
}
data "azurerm_subnet" "kubesubnet" {
name = var.aks_subnet_name
virtual_network_name = azurerm_virtual_network.test.name
resource_group_name = azurerm_resource_group.rg.name
depends_on = [azurerm_virtual_network.test]
}
data "azurerm_subnet" "appgwsubnet" {
name = "appgwsubnet"
virtual_network_name = azurerm_virtual_network.test.name
resource_group_name = azurerm_resource_group.rg.name
depends_on = [azurerm_virtual_network.test]
}
# Public Ip
resource "azurerm_public_ip" "test" {
name = "publicIp1"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
allocation_method = "Static"
sku = "Standard"
tags = var.tags
}
resource "azurerm_application_gateway" "network" {
name = var.app_gateway_name
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
sku {
name = var.app_gateway_sku
tier = "Standard_v2"
capacity = 2
}
gateway_ip_configuration {
name = "appGatewayIpConfig"
subnet_id = data.azurerm_subnet.appgwsubnet.id
}
frontend_port {
name = local.frontend_port_name
port = 80
}
frontend_port {
name = "httpsPort"
port = 443
}
frontend_ip_configuration {
name = local.frontend_ip_configuration_name
public_ip_address_id = azurerm_public_ip.test.id
}
backend_address_pool {
name = local.backend_address_pool_name
}
backend_http_settings {
name = local.http_setting_name
cookie_based_affinity = "Disabled"
port = 80
protocol = "Http"
request_timeout = 1
}
http_listener {
name = local.listener_name
frontend_ip_configuration_name = local.frontend_ip_configuration_name
frontend_port_name = local.frontend_port_name
protocol = "Http"
}
request_routing_rule {
name = local.request_routing_rule_name
rule_type = "Basic"
http_listener_name = local.listener_name
backend_address_pool_name = local.backend_address_pool_name
backend_http_settings_name = local.http_setting_name
}
tags = var.tags
depends_on = [azurerm_virtual_network.test, azurerm_public_ip.test]
}
resource "azurerm_kubernetes_cluster" "k8s" {
name = var.aks_name
location = azurerm_resource_group.rg.location
dns_prefix = var.aks_dns_prefix
resource_group_name = azurerm_resource_group.rg.name
http_application_routing_enabled = false
linux_profile {
admin_username = var.vm_user_name
ssh_key {
key_data = file(var.public_ssh_key_path)
}
}
default_node_pool {
name = "agentpool"
node_count = var.aks_agent_count
vm_size = var.aks_agent_vm_size
os_disk_size_gb = var.aks_agent_os_disk_size
vnet_subnet_id = data.azurerm_subnet.kubesubnet.id
}
service_principal {
client_id = local.client_id
client_secret = local.client_secret
}
network_profile {
network_plugin = "azure"
dns_service_ip = var.aks_dns_service_ip
docker_bridge_cidr = var.aks_docker_bridge_cidr
service_cidr = var.aks_service_cidr
}
role_based_access_control {
enabled = var.aks_enable_rbac
}
depends_on = [azurerm_virtual_network.test, azurerm_application_gateway.network]
tags = var.tags
}
resource "azurerm_role_assignment" "ra1" {
scope = data.azurerm_subnet.kubesubnet.id
role_definition_name = "Network Contributor"
principal_id = local.client_objectid
depends_on = [azurerm_virtual_network.test]
}
resource "azurerm_role_assignment" "ra2" {
scope = azurerm_user_assigned_identity.testIdentity.id
role_definition_name = "Managed Identity Operator"
principal_id = local.client_objectid
depends_on = [azurerm_user_assigned_identity.testIdentity]
}
resource "azurerm_role_assignment" "ra3" {
scope = azurerm_application_gateway.network.id
role_definition_name = "Contributor"
principal_id = azurerm_user_assigned_identity.testIdentity.principal_id
depends_on = [azurerm_user_assigned_identity.testIdentity, azurerm_application_gateway.network]
}
resource "azurerm_role_assignment" "ra4" {
scope = azurerm_resource_group.rg.id
role_definition_name = "Reader"
principal_id = azurerm_user_assigned_identity.testIdentity.principal_id
depends_on = [azurerm_user_assigned_identity.testIdentity, azurerm_application_gateway.network]
}
I have created the id_rsa.pub key like mentioned below
AKS Cluster:
AppGateway:
and I have installed the AGIC by running the following commands
az account set --subscription "Dev3-Tooling"
az aks get-credentials --resource-group "rg-active-stag" --name "aks-cluster1"
kubectl apply -f https://raw.githubusercontent.com/Azure/aad-pod-identity/v1.8.6/deploy/infra/deployment.yaml --insecure-skip-tls-verify
helm repo add application-gateway-kubernetes-ingress https://appgwingress.blob.core.windows.net/ingress-azure-helm-package/
helm repo update
helm install ingress-azure -f helm-config.yaml application-gateway-kubernetes-ingress/ingress-azure --version 1.5.0
Azure Ingress Pod is in the unhealthy state
and I am getting the following error while trying to access the application deployed
I have tried to repro same in my lab environment and got below results.
Step-1:
After provisioning kubernetes cluster and Application Gateway, connect to the cluster using the below command
$ az aks get-credentials -g <resourceGroupName> -n <cluster-name>
Step-2:
Install Azure AD Pod identity for token-based access control
Refer the below command, if your cluster is RBAC enabled.
kubectl create -f https://raw.githubusercontent.com/azure/aad-pod-identity/master/deploy/infra/deployment-rbac.yaml"
Refer the below command, if your cluster is RBAC disabled.
kubectl create -f https://raw.githubusercontent.com/azure/aad-pod-identity/master/deploy/infra/deployment.yaml
Step-3:
Add application gateway ingress controller to the helm repository.
`$helm repo add application-gateway-kubernetes-ingress https://appgwingress.blob.core.windows.net/ingress-azure-helm-package/
$helm repo update
Step-4
Now write helm configuration file and save it to helm-config.yaml as below.
verbosityLevel: 3
appgw:
subscriptionId: <subscriptionId>
resourceGroup: <resourceGroupName>
name: <applicationGatewayName>
shared: false
armAuth:
type: aadPodIdentity
identityResourceID: <identityResourceId>
identityClientID: <identityClientId>
rbac:
enabled: false # true/false
aksClusterConfiguration:
apiServerAddress: <aks-api-server-address>
Step-5:
Now, install AGIC using above file.
$helm install -f helm-config.yaml --generate-name application-gateway-kubernetes-ingress/ingress-azure
After performing the above steps, you can verify ingress service using the below command.
$ kubectl get ingress
Access the IP address in above screenshot, you will be able to access the application.