Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Azure AAD/EntraID access settings #2887

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
22 changes: 21 additions & 1 deletion src/_nebari/stages/infrastructure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ class AzureNodeGroupInputVars(schema.Base):
max_nodes: int


class AADAccessControl(schema.Base):
"""
Represents the configuration for Azure Active Directory Role-Based Access Control
(RBAC) integration in a Kubernetes cluster.

Attributes:
enabled (bool): Indicates whether Azure AD-based Role-Based Access Control is
enabled.
admin_group_object_ids (List[str]): A list of Object IDs of Azure AD groups
assigned administrative roles on the cluster.
"""

azure_rbac_enabled: bool
admin_group_object_ids: List[str]


class AzureInputVars(schema.Base):
name: str
environment: str
Expand All @@ -104,8 +120,9 @@ class AzureInputVars(schema.Base):
tags: Dict[str, str] = {}
max_pods: Optional[int] = None
network_profile: Optional[Dict[str, str]] = None
azure_policy_enabled: bool = None
azure_policy_enabled: Optional[bool] = None
workload_identity_enabled: bool = False
aad_access_control: Optional[AADAccessControl] = None


class AWSAmiTypes(str, enum.Enum):
Expand Down Expand Up @@ -371,6 +388,8 @@ class AzureProvider(schema.Base):
network_profile: Optional[Dict[str, str]] = None
max_pods: Optional[int] = None
workload_identity_enabled: bool = False
azure_policy_enabled: Optional[bool] = None
aad_access_control: Optional[AADAccessControl] = None

@model_validator(mode="before")
@classmethod
Expand Down Expand Up @@ -810,6 +829,7 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]):
network_profile=self.config.azure.network_profile,
max_pods=self.config.azure.max_pods,
workload_identity_enabled=self.config.azure.workload_identity_enabled,
aad_access_control=self.config.azure.aad_access_control,
azure_policy_enabled=self.config.azure.azure_policy_enabled,
).model_dump()
elif self.config.provider == schema.ProviderEnum.aws:
Expand Down
1 change: 1 addition & 0 deletions src/_nebari/stages/infrastructure/template/azure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ module "kubernetes" {
vnet_subnet_id = var.vnet_subnet_id
private_cluster_enabled = var.private_cluster_enabled
workload_identity_enabled = var.workload_identity_enabled
aad_access_control = var.aad_access_control
azure_policy_enabled = var.azure_policy_enabled
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
output "credentials" {
description = "Credentials required for connecting to Kubernetes cluster"
sensitive = true
value = {
endpoint = var.azure_rbac_enabled ? var.kube_admin_config.host : var.kube_config.host
username = var.azure_rbac_enabled ? var.kube_admin_config.username : var.kube_config.username
password = var.azure_rbac_enabled ? var.kube_admin_config.password : var.kube_config.password
client_certificate = var.azure_rbac_enabled ? base64decode(var.kube_admin_config.client_certificate) : base64decode(var.kube_config.client_certificate)
client_key = var.azure_rbac_enabled ? base64decode(var.kube_admin_config.client_key) : base64decode(var.kube_config.client_key)
cluster_ca_certificate = var.azure_rbac_enabled ? base64decode(var.kube_admin_config.cluster_ca_certificate) : base64decode(var.kube_config.cluster_ca_certificate)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
variable "azure_rbac_enabled" {
description = "Flag to enable Azure RBAC"
type = bool
}

variable "kube_admin_config" {
description = "Kube admin config for RBAC"
type = any
sensitive = true
}

variable "kube_config" {
description = "Kube config for standard access"
type = any
sensitive = true
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
data "azurerm_client_config" "current" {
count = var.aad_access_control.azure_rbac_enabled ? 1 : 0
}

# https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster
resource "azurerm_kubernetes_cluster" "main" {
name = var.name
Expand Down Expand Up @@ -64,6 +68,15 @@ resource "azurerm_kubernetes_cluster" "main" {
]
}

dynamic "azure_active_directory_role_based_access_control" {
for_each = var.aad_access_control.azure_rbac_enabled ? [var.aad_access_control] : []
content {
azure_rbac_enabled = var.aad_access_control.azure_rbac_enabled
admin_group_object_ids = var.aad_access_control.admin_group_object_ids
tenant_id = data.azurerm_client_config.current[0].tenant_id
managed = true
}
}
}

# https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster_node_pool
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
module "k8s_credentials" {
source = "../credentials"
azure_rbac_enabled = var.aad_access_control.azure_rbac_enabled
kube_admin_config = azurerm_kubernetes_cluster.main.kube_admin_config[0]
kube_config = azurerm_kubernetes_cluster.main.kube_config[0]
}

output "credentials" {
description = "Credentials required for connecting to kubernetes cluster"
description = "Credentials required for connecting to Kubernetes cluster"
sensitive = true
value = {
# see bottom of https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster
endpoint = azurerm_kubernetes_cluster.main.kube_config.0.host
username = azurerm_kubernetes_cluster.main.kube_config.0.username
password = azurerm_kubernetes_cluster.main.kube_config.0.password
client_certificate = base64decode(azurerm_kubernetes_cluster.main.kube_config.0.client_certificate)
client_key = base64decode(azurerm_kubernetes_cluster.main.kube_config.0.client_key)
cluster_ca_certificate = base64decode(azurerm_kubernetes_cluster.main.kube_config.0.cluster_ca_certificate)
}
value = module.k8s_credentials.credentials
}

output "kubeconfig" {
description = "Kubernetes connection kubeconfig"
sensitive = true
value = azurerm_kubernetes_cluster.main.kube_config_raw
value = var.aad_access_control.azure_rbac_enabled ? azurerm_kubernetes_cluster.main.kube_admin_config_raw : azurerm_kubernetes_cluster.main.kube_config_raw
}

output "cluster_oidc_issuer_url" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ variable "workload_identity_enabled" {
default = false
}

variable "aad_access_control" {
description = "Azure Active Directory Role-Based Access Control (RBAC) integration in a Kubernetes cluster"
type = object({
azure_rbac_enabled : bool
admin_group_object_ids : list(string)
})
default = {
azure_rbac_enabled : false
admin_group_object_ids : []
}
nullable = false
}

variable "azure_policy_enabled" {
description = "Enable Azure Policy"
type = bool
Expand Down
13 changes: 13 additions & 0 deletions src/_nebari/stages/infrastructure/template/azure/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,20 @@ variable "workload_identity_enabled" {
default = false
}

variable "aad_access_control" {
description = "Azure Active Directory Role-Based Access Control (RBAC) integration in a Kubernetes cluster"
type = object({
azure_rbac_enabled : bool
admin_group_object_ids : list(string)
})
default = {
azure_rbac_enabled : false
admin_group_object_ids : []
}
}

variable "azure_policy_enabled" {
description = "Enable Azure Policy"
type = bool
default = false
}
25 changes: 25 additions & 0 deletions src/_nebari/stages/tf_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,31 @@ def NebariKubernetesProvider(nebari_config: schema.Main):
token="${data.aws_eks_cluster_auth.default.token}",
),
)

# if (
# nebari_config.provider == "azure"
# ): # we need to also check for disabled local account
# return deep_merge(
# Data("azurerm_kubernetes_cluster", "default", name=cluster_name),
# Provider(
# "kubernetes",
# host="${data.azurerm_kubernetes_cluster.default.kube_config.0.host}",
# cluster_ca_certificate="${base64decode(data.azurerm_kubernetes_cluster.default.kube_config.0.cluster_ca_certificate)}",
# exec={
# "api_version": "client.authentication.k8s.io/v1beta1",
# "command": "az",
# "args": [
# "aks",
# "get-credentials",
# "--resource-group",
# nebari_config.azure.resource_group_name,
# "--name",
# nebari_config.azure.cluster_name,
# "--overwrite-existing",
# ],
# },
# ),
# )
return Provider(
"kubernetes",
)
Expand Down
Loading