Skip to content

Commit fcc9d29

Browse files
authored
Merge pull request #3 from quixio/azure/supportedRoutingTraffic
Azure/supported routing traffic using UDR
2 parents fc22bf7 + e91507f commit fcc9d29

File tree

13 files changed

+135
-102
lines changed

13 files changed

+135
-102
lines changed

BASTION_ACCESS.md

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,9 @@ This guide shows how to connect to a private AKS cluster using Azure Bastion, ei
99
- Azure CLI installed locally
1010
- Your SSH private key corresponding to jumpbox_ssh_public_key
1111

12-
## Option A: Run kubectl from the Jumpbox
1312

14-
1. Open an SSH session to the jumpbox via Bastion (Native client):
15-
```bash
16-
export RG="<RESOURCE_GROUP>"
17-
export BASTION="<BASTION_NAME>"
18-
export VM="<JUMPBOX_NAME>"
19-
export SSH_KEY="$HOME/.ssh/id_rsa"
20-
21-
VM_ID=$(az vm show -g "$RG" -n "$VM" --query id -o tsv)
22-
az network bastion ssh \
23-
--name "$BASTION" \
24-
--resource-group "$RG" \
25-
--target-resource-id "$VM_ID" \
26-
--auth-type ssh-key --username <ADMIN_USERNAME> --ssh-key $SSH_KEY
27-
```
28-
29-
2. Inside the VM, authenticate and fetch kubeconfig:
30-
```bash
31-
az login --use-device-code
32-
az account set --subscription "<SUBSCRIPTION_ID>"
33-
az aks get-credentials -g "<RESOURCE_GROUP>" -n "<AKS_NAME>" --overwrite-existing
34-
kubectl get nodes -o wide
35-
```
3613

37-
## Option B: Run kubectl from your local machine (Bastion tunneling)
14+
## Run kubectl from your local machine (Bastion tunneling)
3815

3916
1. Start SSH tunnel to the VM via Bastion:
4017
```bash

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,28 @@ module "quix_aks" {
4646
}
4747
```
4848

49+
### Private DNS Zone for Private Clusters
50+
51+
When deploying a private AKS cluster, you can control how the Private DNS Zone is managed using the `private_dns_zone_id` variable:
52+
53+
```hcl
54+
module "quix_aks" {
55+
# ...
56+
private_cluster_enabled = true
57+
58+
# Option 1: Let AKS manage the Private DNS Zone automatically (default)
59+
private_dns_zone_id = "System"
60+
61+
# Option 2: Disable Private DNS Zone management (manual DNS configuration required)
62+
# private_dns_zone_id = "None"
63+
64+
# Option 3: Use an existing Private DNS Zone (BYO)
65+
# private_dns_zone_id = "/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.Network/privateDnsZones/privatelink.<region>.azmk8s.io"
66+
}
67+
```
68+
69+
**Note:** When using an existing Private DNS Zone (Option 3), the module automatically assigns the `Private DNS Zone Contributor` role to the AKS cluster identity.
70+
4971
## Tiered Storage module (tiered-storage)
5072

5173
Module documentation (inputs/outputs/resources):
@@ -115,7 +137,7 @@ module "quix_aks" {
115137
nodes_subnet_name = "Subnet-Nodes"
116138
nodes_subnet_cidr = "10.240.0.0/22"
117139
118-
nat_identity_name = "my-nat-id"
140+
identity_name = "my-nat-id"
119141
public_ip_name = "my-nat-ip"
120142
nat_gateway_name = "my-nat"
121143
availability_zone = "1"

examples/private-quix-infr-external-vnet/main.tf

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,10 @@ module "aks" {
7979
sku_tier = "Standard"
8080
private_cluster_enabled = true
8181

82-
vnet_name = "vnet-quix-private"
83-
vnet_address_space = ["10.240.0.0/16"]
84-
nodes_subnet_name = "Subnet-Nodes"
85-
nodes_subnet_cidr = "10.240.0.0/22"
82+
vnet_name = azurerm_virtual_network.ext.name
83+
nodes_subnet_name = azurerm_subnet.nodes_ext.name
8684

87-
# Reuse external VNet/Subnets
88-
vnet_id = azurerm_virtual_network.ext.id
89-
nodes_subnet_id = azurerm_subnet.nodes_ext.id
90-
91-
nat_identity_name = "quix-private-nat-id"
85+
identity_name = "quix-private-nat-id"
9286
public_ip_name = "quix-private-nat-ip"
9387
nat_gateway_name = "quix-private-nat"
9488
availability_zone = "2"
@@ -117,8 +111,6 @@ module "aks" {
117111
create_bastion_subnet = false
118112
enable_bastion = true
119113
bastion_name = "quix-bastion"
120-
# Reuse external Bastion subnet & IP
121-
bastion_subnet_id = azurerm_subnet.bastion_ext.id
122114

123115
jumpbox_name = "quix-jumpbox"
124116
jumpbox_vm_size = "Standard_B2s"

examples/private-quix-infr/main.tf

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,60 @@ resource "azurerm_resource_group" "this" {
2020
module "aks" {
2121
source = "../../modules/quix-aks"
2222

23+
# Core + RG
2324
name = "quix-aks-private"
2425
location = "westeurope"
2526
resource_group_name = "rg-quix-private"
2627
create_resource_group = false
2728
kubernetes_version = "1.32.4"
2829
sku_tier = "Standard"
2930
private_cluster_enabled = true
30-
31+
# Use existing Private DNS Zone for the AKS private API server:
32+
# - "System" lets AKS manage it automatically (default)
33+
# - "None" disables creation/association (you must manage DNS yourself)
34+
# - "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Network/privateDnsZones/privatelink.westeurope.azmk8s.io"
35+
# to reuse an existing zone
36+
private_dns_zone_id = "System"
37+
38+
# Networking (VNet/Subnet)
3139
vnet_name = "vnet-quix-private"
3240
vnet_address_space = ["10.240.0.0/16"]
3341
nodes_subnet_name = "Subnet-Nodes"
3442
nodes_subnet_cidr = "10.240.0.0/22"
3543

36-
nat_identity_name = "quix-private-nat-id"
44+
# Network profile
45+
network_profile = {
46+
network_plugin_mode = "overlay"
47+
service_cidr = "172.22.0.0/16"
48+
dns_service_ip = "172.22.0.10"
49+
pod_cidr = "10.144.0.0/16"
50+
}
51+
52+
# NAT (names reserved even if not used with userDefinedRouting)
53+
identity_name = "quix-private-nat-id"
3754
public_ip_name = "quix-private-nat-ip"
3855
nat_gateway_name = "quix-private-nat"
3956
availability_zone = "2"
4057

41-
enable_credentials_fetch = true
58+
# Bastion
59+
create_bastion_subnet = true
60+
enable_bastion = true
61+
bastion_subnet_cidr = "10.240.5.0/27"
62+
bastion_name = "quix-bastion"
63+
bastion_public_ip_name = "quix-bastion-ip"
64+
65+
# Jumpbox
66+
jumpbox_name = "quix-jumpbox"
67+
jumpbox_vm_size = "Standard_B2s"
68+
jumpbox_admin_username = "azureuser"
69+
jumpbox_ssh_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC3Zz+tHUEI7ulzE69GxtLwi9DOvROBG4aI7h3za2FAP6Ya9/GhG2zcBiKOzk3SlKavE3/5NomgGifTC/ica6rTPlpb4U5oky/2phs9AtczVVI2G+yNC43hJzVhWbqKT3qAGGCGEm2+Cpxx7spKEbZAfAcq5GxL3k9kTcpaQEv3hpVvqK3zlCziHyahUv1pxQGuX3b2hqi4idgFX3m0FaqU98DtQu/I9x95jXHrb7Wltp3sbTSKCDxGo3nk4plpzILs/OqTMSPpfxwarCXA1ZtU82hyWO4Szn2U4I+MbuNaO/dso1oNlprqJQgsQ8t+hawCdIHeZ00M/QELdnYldBjo1jM19AT1OwMcB7PP7GRTNv7YsDW10YCvX9XRPab66PIKpe5R4IG/n6TzEwUP2pb4hRJWvnPJzrHK5HEJg7G7baCEyjCtaWkL4M7dBxIGJ3sp9IfjdeztV2Llh+hYmwPefTejprER+Q/qHZTNr1wEW4BV0TQQd+jeqdIL4QkIno3IyM3IBX+uPM/WlSpi2sT+hDqiUcCRu/x21O/bVYz/UbeHIqptRDfGc5rVoAN/zc/kGsGeGuP3auyI6aQxlnU0wMDdyS8rf3SpWagOB2UFNxZSuU2gnYdtz2uWG4vF75Sqr04MFJImHIY4N7gHJrvdarg6YBaDDnmdREcqp3ooAw=="
70+
71+
# Features
72+
oidc_issuer_enabled = true
73+
workload_identity_enabled = true
74+
enable_credentials_fetch = true
75+
76+
# Node pools
4277
node_pools = {
4378
default = {
4479
name = "default"
@@ -56,32 +91,13 @@ module "aks" {
5691
}
5792
}
5893

59-
network_profile = {
60-
network_plugin_mode = "overlay"
61-
service_cidr = "172.22.0.0/16"
62-
dns_service_ip = "172.22.0.10"
63-
pod_cidr = "10.144.0.0/16"
64-
}
65-
66-
enable_bastion = true
67-
bastion_subnet_cidr = "10.240.5.0/27"
68-
bastion_name = "quix-bastion"
69-
bastion_public_ip_name = "quix-bastion-ip"
70-
71-
jumpbox_name = "quix-jumpbox"
72-
jumpbox_vm_size = "Standard_B2s"
73-
jumpbox_admin_username = "azureuser"
74-
jumpbox_ssh_public_key = "ssh-rsa ......"
75-
76-
oidc_issuer_enabled = true
77-
workload_identity_enabled = true
78-
79-
94+
# Tags
8095
tags = {
8196
environment = "demo"
8297
project = "Quix"
8398
}
8499

100+
# Dependencies
85101
depends_on = [azurerm_resource_group.this]
86102
}
87103

examples/public-quix-infr-tiered-storage/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ module "aks" {
2727
nodes_subnet_name = "Subnet-Nodes"
2828
nodes_subnet_cidr = "10.240.0.0/22"
2929

30-
nat_identity_name = "quix-public-nat-id"
30+
identity_name = "quix-public-nat-id"
3131
public_ip_name = "quix-public-nat-ip"
3232
nat_gateway_name = "quix-public-nat"
3333
availability_zone = "1"

examples/public-quix-infr/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ module "aks" {
2727
nodes_subnet_name = "Subnet-Nodes"
2828
nodes_subnet_cidr = "10.240.0.0/22"
2929

30-
nat_identity_name = "quix-public-nat-id"
30+
identity_name = "quix-public-nat-id"
3131
public_ip_name = "quix-public-nat-ip"
3232
nat_gateway_name = "quix-public-nat"
3333
availability_zone = "1"

modules/quix-aks/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ No modules.
7474
| <a name="input_name"></a> [name](#input\_name) | Name of the AKS cluster | `string` | n/a | yes |
7575
| <a name="input_nat_gateway_id"></a> [nat\_gateway\_id](#input\_nat\_gateway\_id) | Existing NAT Gateway ID to associate when create\_nat is false | `string` | `null` | no |
7676
| <a name="input_nat_gateway_name"></a> [nat\_gateway\_name](#input\_nat\_gateway\_name) | Name of the NAT Gateway | `string` | n/a | yes |
77-
| <a name="input_nat_identity_name"></a> [nat\_identity\_name](#input\_nat\_identity\_name) | Name of the managed identity for NAT | `string` | n/a | yes |
77+
| <a name="input_identity_name"></a> [identity_name](#input_identity_name) | Name of the user-assigned managed identity for the AKS cluster | `string` | n/a | yes |
7878
| <a name="input_network_profile"></a> [network\_profile](#input\_network\_profile) | AKS network profile | <pre>object({<br/> network_plugin_mode = string # "overlay" or "vnet"<br/> service_cidr = string<br/> dns_service_ip = string<br/> pod_cidr = optional(string)<br/> network_policy = optional(string, "calico")<br/> outbound_type = optional(string, "userAssignedNATGateway")<br/> })</pre> | n/a | yes |
7979
| <a name="input_node_pools"></a> [node\_pools](#input\_node\_pools) | Map of additional node pools (include a 'system' pool to override default) | <pre>map(object({<br/> name = string<br/> type = string # system | user<br/> node_count = number<br/> vm_size = string<br/> max_pods = optional(number)<br/> taints = optional(list(string))<br/> labels = optional(map(string))<br/> mode = optional(string) # system | user (overrides type)<br/> }))</pre> | `{}` | no |
8080
| <a name="input_nodes_subnet_cidr"></a> [nodes\_subnet\_cidr](#input\_nodes\_subnet\_cidr) | CIDR for the AKS nodes subnet | `string` | n/a | yes |

modules/quix-aks/aks.tf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ resource "azurerm_kubernetes_cluster" "this" {
1010
kubernetes_version = var.kubernetes_version
1111
sku_tier = var.sku_tier
1212
private_cluster_enabled = var.private_cluster_enabled
13+
private_dns_zone_id = var.private_cluster_enabled ? var.private_dns_zone_id : null
1314

1415
oidc_issuer_enabled = var.oidc_issuer_enabled
1516
workload_identity_enabled = var.workload_identity_enabled
@@ -18,7 +19,7 @@ resource "azurerm_kubernetes_cluster" "this" {
1819
name = local.system_pool_name
1920
node_count = local.system_pool.node_count
2021
vm_size = local.system_pool.vm_size
21-
vnet_subnet_id = coalesce(try(azurerm_subnet.nodes[0].id, null), var.nodes_subnet_id)
22+
vnet_subnet_id = coalesce(try(azurerm_subnet.nodes[0].id, null), try(data.azurerm_subnet.existing[0].id, null))
2223

2324
upgrade_settings {
2425
max_surge = "10%"
@@ -67,7 +68,7 @@ resource "azurerm_kubernetes_cluster_node_pool" "additional" {
6768
kubernetes_cluster_id = azurerm_kubernetes_cluster.this.id
6869
vm_size = each.value.vm_size
6970
node_count = each.value.node_count
70-
vnet_subnet_id = coalesce(try(azurerm_subnet.nodes[0].id, null), var.nodes_subnet_id)
71+
vnet_subnet_id = coalesce(try(azurerm_subnet.nodes[0].id, null), try(data.azurerm_subnet.existing[0].id, null))
7172
mode = lower(coalesce(each.value.mode, each.value.type)) == "system" ? "System" : "User"
7273
node_taints = coalesce(each.value.taints, null)
7374
orchestrator_version = var.kubernetes_version

modules/quix-aks/bastion.tf

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@
55
resource "azurerm_subnet" "bastion" {
66
count = var.enable_bastion && var.create_bastion_subnet ? 1 : 0
77
name = "AzureBastionSubnet"
8-
resource_group_name = local.rg_name_effective
8+
resource_group_name = local.vnet_rg_effective
99
virtual_network_name = coalesce(try(azurerm_virtual_network.this[0].name, null), try(data.azurerm_virtual_network.existing[0].name, null), var.vnet_name)
1010
address_prefixes = [var.bastion_subnet_cidr]
1111
}
1212

13-
data "azurerm_subnet" "bastion" {
13+
data "azurerm_subnet" "existing_bastion" {
1414
count = var.enable_bastion && !var.create_bastion_subnet ? 1 : 0
1515
name = "AzureBastionSubnet"
1616
virtual_network_name = var.vnet_name
17-
resource_group_name = local.rg_name_effective
17+
resource_group_name = local.vnet_rg_effective
1818
}
1919

2020
resource "azurerm_public_ip" "bastion" {
2121
count = var.enable_bastion && (var.bastion_public_ip_id == null) ? 1 : 0
2222
name = var.bastion_public_ip_name
2323
location = local.rg_location
24-
resource_group_name = local.rg_name_effective
24+
resource_group_name = local.vnet_rg_effective
2525
allocation_method = "Static"
2626
sku = "Standard"
2727
tags = var.tags
@@ -31,14 +31,14 @@ resource "azurerm_bastion_host" "this" {
3131
count = var.enable_bastion ? 1 : 0
3232
name = var.bastion_name
3333
location = local.rg_location
34-
resource_group_name = local.rg_name_effective
34+
resource_group_name = local.vnet_rg_effective
3535
sku = "Standard"
3636
tunneling_enabled = true
3737
ip_connect_enabled = true
3838

3939
ip_configuration {
4040
name = "configuration"
41-
subnet_id = coalesce(try(azurerm_subnet.bastion[0].id, null), try(data.azurerm_subnet.bastion[0].id, null), var.bastion_subnet_id)
41+
subnet_id = coalesce(try(azurerm_subnet.bastion[0].id, null), try(data.azurerm_subnet.existing_bastion[0].id, null))
4242
public_ip_address_id = coalesce(try(azurerm_public_ip.bastion[0].id, null), var.bastion_public_ip_id)
4343
}
4444

@@ -49,11 +49,11 @@ resource "azurerm_network_interface" "jumpbox" {
4949
count = var.enable_bastion ? 1 : 0
5050
name = "${var.jumpbox_name}-nic"
5151
location = local.rg_location
52-
resource_group_name = local.rg_name_effective
52+
resource_group_name = local.vnet_rg_effective
5353

5454
ip_configuration {
5555
name = "ipconfig1"
56-
subnet_id = coalesce(try(azurerm_subnet.nodes[0].id, null), data.azurerm_subnet.nodes[0].id, var.nodes_subnet_id)
56+
subnet_id = coalesce(try(azurerm_subnet.nodes[0].id, null), try(data.azurerm_subnet.existing[0].id, null))
5757
private_ip_address_allocation = "Dynamic"
5858
}
5959

@@ -64,7 +64,7 @@ resource "azurerm_linux_virtual_machine" "jumpbox" {
6464
count = var.enable_bastion ? 1 : 0
6565
name = var.jumpbox_name
6666
location = local.rg_location
67-
resource_group_name = local.rg_name_effective
67+
resource_group_name = local.vnet_rg_effective
6868
size = var.jumpbox_vm_size
6969
admin_username = var.jumpbox_admin_username
7070

modules/quix-aks/network.tf

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,44 @@
22
# Networking: VNet, Subnets, NAT Gateway and Identity
33
################################################################################
44

5+
locals {
6+
# When provided, place/read VNet and Subnets from this resource group
7+
vnet_rg_effective = coalesce(var.vnet_resource_group, local.rg_name_effective)
8+
}
9+
510
resource "azurerm_virtual_network" "this" {
611
count = var.create_vnet ? 1 : 0
712
name = var.vnet_name
813
location = local.rg_location
9-
resource_group_name = local.rg_name_effective
14+
resource_group_name = local.vnet_rg_effective
1015
address_space = var.vnet_address_space
1116
tags = var.tags
1217
}
1318

1419
data "azurerm_virtual_network" "existing" {
1520
count = var.create_vnet ? 0 : 1
1621
name = var.vnet_name
17-
resource_group_name = local.rg_name_effective
22+
resource_group_name = local.vnet_rg_effective
1823
}
1924

2025
resource "azurerm_subnet" "nodes" {
2126
count = var.create_nodes_subnet ? 1 : 0
2227
name = var.nodes_subnet_name
23-
resource_group_name = local.rg_name_effective
28+
resource_group_name = local.vnet_rg_effective
2429
virtual_network_name = coalesce(try(azurerm_virtual_network.this[0].name, null), try(data.azurerm_virtual_network.existing[0].name, null), var.vnet_name)
2530
address_prefixes = [var.nodes_subnet_cidr]
2631
service_endpoints = ["Microsoft.Storage"]
2732
}
2833

29-
data "azurerm_subnet" "nodes" {
34+
data "azurerm_subnet" "existing" {
3035
count = var.create_nodes_subnet ? 0 : 1
3136
name = var.nodes_subnet_name
3237
virtual_network_name = var.vnet_name
33-
resource_group_name = local.rg_name_effective
38+
resource_group_name = local.vnet_rg_effective
3439
}
3540

3641
resource "azurerm_user_assigned_identity" "nat_identity" {
37-
name = var.nat_identity_name
42+
name = var.identity_name
3843
location = local.rg_location
3944
resource_group_name = local.rg_name_effective
4045
tags = var.tags
@@ -68,7 +73,7 @@ resource "azurerm_nat_gateway_public_ip_association" "this" {
6873

6974
resource "azurerm_subnet_nat_gateway_association" "nodes" {
7075
count = (var.create_nodes_subnet || var.create_nat) ? 1 : 0
71-
subnet_id = coalesce(try(azurerm_subnet.nodes[0].id, null), try(data.azurerm_subnet.nodes[0].id, null), var.nodes_subnet_id)
76+
subnet_id = coalesce(try(azurerm_subnet.nodes[0].id, null), try(data.azurerm_subnet.existing[0].id, null))
7277
nat_gateway_id = coalesce(try(azurerm_nat_gateway.this[0].id, null), var.nat_gateway_id)
7378
}
7479

0 commit comments

Comments
 (0)