Skip to content

Commit 4bb32bc

Browse files
chris-rockclaude
andcommitted
docs: add Terraform configs for WIF testing infrastructure
Add Terraform modules under tests/terraform/wif/ for automated provisioning of WIF test clusters (GKE, GKE Autopilot, EKS, AKS). Each module creates a management + target cluster pair with all necessary IAM/RBAC plumbing and outputs a MondooAuditConfig snippet. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 56f2997 commit 4bb32bc

19 files changed

Lines changed: 998 additions & 0 deletions

docs/development.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ subjects:
385385
- **EKS**: Configure IRSA on management cluster, create IAM trust policy, grant role cluster access
386386
- **AKS**: Install Azure Workload Identity, create federated identity credential, grant managed identity RBAC on target cluster
387387

388+
> **Terraform automation:** Terraform configurations for provisioning WIF test infrastructure (GKE, GKE Autopilot, EKS, AKS) are available in `tests/terraform/wif/`. See [Test Methods 4-6](#test-method-4-aks-azure-workload-identity) for cloud-specific instructions, or the [WIF Terraform README](../tests/terraform/wif/README.md) for the automated alternative.
389+
388390
**Additional External Cluster Options:**
389391

390392
Each external cluster also supports these optional fields:
@@ -1045,6 +1047,8 @@ This method tests Workload Identity Federation (WIF) with Azure AKS. The managem
10451047
uses Azure AD Workload Identity to authenticate to a target AKS cluster without static credentials.
10461048

10471049
> **Note:** This requires Azure resources and two AKS clusters. It cannot be tested locally with k3d.
1050+
>
1051+
> **Terraform alternative:** Instead of the manual steps below, you can use `tests/terraform/wif/aks/` to create both clusters and all IAM/RBAC plumbing automatically. See the [WIF Terraform README](../tests/terraform/wif/README.md).
10481052

10491053
**Key AKS-specific details:**
10501054
- ServiceAccount annotation: `azure.workload.identity/client-id`
@@ -1335,6 +1339,8 @@ This method tests Workload Identity using AWS EKS IRSA. The management cluster u
13351339
for Service Accounts to authenticate to a target EKS cluster without static credentials.
13361340

13371341
> **Note:** This requires AWS resources and two EKS clusters. It cannot be tested locally with k3d.
1342+
>
1343+
> **Terraform alternative:** Instead of the manual steps below, you can use `tests/terraform/wif/eks/` to create both clusters, the VPC, and all IAM plumbing automatically. See the [WIF Terraform README](../tests/terraform/wif/README.md).
13381344

13391345
**Key EKS-specific details:**
13401346
- ServiceAccount annotation: `eks.amazonaws.com/role-arn`
@@ -1687,6 +1693,8 @@ This method tests Workload Identity Federation with Google Kubernetes Engine. Th
16871693
uses GCP Workload Identity to authenticate to a target GKE cluster without static credentials.
16881694

16891695
> **Note:** This requires GCP resources and two GKE clusters. It cannot be tested locally with k3d.
1696+
>
1697+
> **Terraform alternative:** Instead of the manual steps below, you can use `tests/terraform/wif/gke/` (or `gke-autopilot/` for Autopilot clusters) to create both clusters and all IAM plumbing automatically. See the [WIF Terraform README](../tests/terraform/wif/README.md).
16901698

16911699
**Key GKE-specific details:**
16921700
- ServiceAccount annotation: `iam.gke.io/gcp-service-account`

tests/terraform/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.terraform/
2+
.terraform.lock.hcl
3+
terraform.tfstate
4+
terraform.tfstate.backup
5+
kubeconfig-*
6+
*.tfplan

tests/terraform/wif/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# WIF Testing Terraform
2+
3+
Terraform configurations for provisioning cloud infrastructure to test Workload Identity Federation (WIF) with the Mondoo Operator.
4+
5+
Each subdirectory creates a **management cluster** (where the operator runs) and a **target cluster** (to be scanned), along with the IAM/RBAC plumbing needed for WIF authentication.
6+
7+
## Prerequisites
8+
9+
- [Terraform](https://developer.hashicorp.com/terraform/install) >= 1.3
10+
- Cloud provider CLI authenticated:
11+
- **GKE**: `gcloud auth application-default login`
12+
- **EKS**: `aws configure` or environment variables
13+
- **AKS**: `az login`
14+
15+
## Usage
16+
17+
Each provider is independent. Navigate to the desired directory and run:
18+
19+
```bash
20+
cd gke/ # or eks/ or aks/
21+
terraform init
22+
terraform plan
23+
terraform apply
24+
```
25+
26+
After `apply` completes, check the outputs for the `mondoo_audit_config_snippet` which shows the exact YAML to use in your `MondooAuditConfig`.
27+
28+
```bash
29+
terraform output mondoo_audit_config_snippet
30+
```
31+
32+
## Providers
33+
34+
### GKE (`gke/`)
35+
36+
Creates two GKE Standard clusters with Workload Identity enabled. Sets up a Google Service Account with the necessary IAM bindings for the management cluster's KSA to authenticate to the target cluster.
37+
38+
**Required variables:**
39+
- `project_id` - GCP project ID
40+
41+
**Post-apply manual step:** Create a `ClusterRoleBinding` on the target cluster granting the GSA read access (shown in outputs).
42+
43+
### GKE Autopilot (`gke-autopilot/`)
44+
45+
Same as GKE but uses Autopilot clusters. Autopilot clusters have Workload Identity enabled by default and manage node pools automatically.
46+
47+
**Required variables:**
48+
- `project_id` - GCP project ID
49+
50+
**Post-apply manual step:** Create a `ClusterRoleBinding` on the target cluster granting the GSA read access (shown in outputs).
51+
52+
### EKS (`eks/`)
53+
54+
Creates two EKS clusters in a shared VPC with IRSA (IAM Roles for Service Accounts) configured. Sets up an IAM role with a trust policy for the management cluster's OIDC provider and maps it into the target cluster's `aws-auth` ConfigMap.
55+
56+
**Required variables:** None (uses defaults)
57+
58+
### AKS (`aks/`)
59+
60+
Creates two AKS clusters with Azure Workload Identity configured. Sets up an Azure AD application with a federated identity credential and grants it RBAC on the target cluster.
61+
62+
**Required variables:** None (uses defaults)
63+
64+
## Cleanup
65+
66+
```bash
67+
terraform destroy
68+
```
69+
70+
## Design Notes
71+
72+
- Spot/preemptible instances are used for cost savings
73+
- Random 4-character suffixes ensure unique resource names
74+
- Terraform state is stored locally (not in a remote backend)
75+
- These configs are for manual developer testing, not CI

tests/terraform/wif/aks/main.tf

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
resource "random_string" "suffix" {
2+
length = 4
3+
special = false
4+
upper = false
5+
}
6+
7+
locals {
8+
name_prefix = "mondoo-wif-${random_string.suffix.result}"
9+
}
10+
11+
data "azurerm_subscription" "current" {}
12+
data "azurerm_client_config" "current" {}
13+
14+
################################################################################
15+
# Resource Group
16+
################################################################################
17+
18+
resource "azurerm_resource_group" "rg" {
19+
name = "${local.name_prefix}-rg"
20+
location = var.location
21+
22+
tags = {
23+
Environment = "Mondoo Operator WIF Tests"
24+
}
25+
}
26+
27+
################################################################################
28+
# Management Cluster (runs the operator, OIDC + Workload Identity enabled)
29+
################################################################################
30+
31+
resource "azurerm_kubernetes_cluster" "management" {
32+
name = "${local.name_prefix}-mgmt"
33+
location = azurerm_resource_group.rg.location
34+
resource_group_name = azurerm_resource_group.rg.name
35+
dns_prefix = "${local.name_prefix}-mgmt"
36+
kubernetes_version = var.k8s_version
37+
38+
oidc_issuer_enabled = true
39+
workload_identity_enabled = true
40+
41+
default_node_pool {
42+
name = "default"
43+
node_count = 1
44+
vm_size = "Standard_D2s_v3"
45+
}
46+
47+
identity {
48+
type = "SystemAssigned"
49+
}
50+
51+
tags = {
52+
Environment = "Mondoo Operator WIF Tests"
53+
}
54+
}
55+
56+
################################################################################
57+
# Target Cluster (to be scanned, Azure RBAC enabled)
58+
################################################################################
59+
60+
resource "azurerm_kubernetes_cluster" "target" {
61+
name = "${local.name_prefix}-target"
62+
location = azurerm_resource_group.rg.location
63+
resource_group_name = azurerm_resource_group.rg.name
64+
dns_prefix = "${local.name_prefix}-target"
65+
kubernetes_version = var.k8s_version
66+
67+
azure_active_directory_role_based_access_control {
68+
managed = true
69+
azure_rbac_enabled = true
70+
}
71+
72+
default_node_pool {
73+
name = "default"
74+
node_count = 1
75+
vm_size = "Standard_D2s_v3"
76+
}
77+
78+
identity {
79+
type = "SystemAssigned"
80+
}
81+
82+
tags = {
83+
Environment = "Mondoo Operator WIF Tests"
84+
}
85+
}
86+
87+
################################################################################
88+
# Azure AD Application + Service Principal for the scanner
89+
################################################################################
90+
91+
resource "azuread_application" "scanner" {
92+
display_name = "${local.name_prefix}-scanner"
93+
}
94+
95+
resource "azuread_service_principal" "scanner" {
96+
client_id = azuread_application.scanner.client_id
97+
}
98+
99+
# Federated identity credential: links the management cluster's KSA to the Azure AD app
100+
resource "azuread_application_federated_identity_credential" "scanner" {
101+
application_id = azuread_application.scanner.id
102+
display_name = "mondoo-operator-wif"
103+
audiences = ["api://AzureADTokenExchange"]
104+
issuer = azurerm_kubernetes_cluster.management.oidc_issuer_url
105+
subject = "system:serviceaccount:${var.scanner_namespace}:${var.scanner_service_account}"
106+
}
107+
108+
################################################################################
109+
# Role Assignments on the target cluster
110+
################################################################################
111+
112+
# Allow the service principal to get cluster credentials
113+
resource "azurerm_role_assignment" "cluster_user" {
114+
scope = azurerm_kubernetes_cluster.target.id
115+
role_definition_name = "Azure Kubernetes Service Cluster User Role"
116+
principal_id = azuread_service_principal.scanner.object_id
117+
}
118+
119+
# Allow the service principal to read K8s resources via Azure RBAC
120+
resource "azurerm_role_assignment" "rbac_reader" {
121+
scope = azurerm_kubernetes_cluster.target.id
122+
role_definition_name = "Azure Kubernetes Service RBAC Reader"
123+
principal_id = azuread_service_principal.scanner.object_id
124+
}

tests/terraform/wif/aks/outputs.tf

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
output "subscription_id" {
2+
description = "Azure subscription ID."
3+
value = data.azurerm_subscription.current.subscription_id
4+
}
5+
6+
output "tenant_id" {
7+
description = "Azure AD tenant ID."
8+
value = data.azurerm_client_config.current.tenant_id
9+
}
10+
11+
output "client_id" {
12+
description = "Azure AD application (client) ID for the scanner."
13+
value = azuread_application.scanner.client_id
14+
}
15+
16+
output "resource_group" {
17+
description = "Resource group containing both clusters."
18+
value = azurerm_resource_group.rg.name
19+
}
20+
21+
output "management_cluster_name" {
22+
description = "Name of the management AKS cluster."
23+
value = azurerm_kubernetes_cluster.management.name
24+
}
25+
26+
output "target_cluster_name" {
27+
description = "Name of the target AKS cluster."
28+
value = azurerm_kubernetes_cluster.target.name
29+
}
30+
31+
output "mondoo_audit_config_snippet" {
32+
description = "MondooAuditConfig YAML snippet for the AKS WIF external cluster."
33+
value = <<-EOT
34+
externalClusters:
35+
- name: ${azurerm_kubernetes_cluster.target.name}
36+
workloadIdentity:
37+
provider: aks
38+
aks:
39+
subscriptionId: ${data.azurerm_subscription.current.subscription_id}
40+
resourceGroup: ${azurerm_resource_group.rg.name}
41+
clusterName: ${azurerm_kubernetes_cluster.target.name}
42+
clientId: ${azuread_application.scanner.client_id}
43+
tenantId: ${data.azurerm_client_config.current.tenant_id}
44+
EOT
45+
}
46+
47+
output "kubeconfig_commands" {
48+
description = "Commands to configure kubectl for both clusters."
49+
value = <<-EOT
50+
# Management cluster:
51+
az aks get-credentials --resource-group ${azurerm_resource_group.rg.name} --name ${azurerm_kubernetes_cluster.management.name} --context mgmt
52+
53+
# Target cluster:
54+
az aks get-credentials --resource-group ${azurerm_resource_group.rg.name} --name ${azurerm_kubernetes_cluster.target.name} --context target
55+
EOT
56+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
variable "location" {
2+
description = "Azure region for all resources."
3+
type = string
4+
default = "eastus"
5+
}
6+
7+
variable "k8s_version" {
8+
description = "Kubernetes version for the AKS clusters."
9+
type = string
10+
default = "1.30"
11+
}
12+
13+
variable "scanner_namespace" {
14+
description = "Namespace where the Mondoo operator scanner runs."
15+
type = string
16+
default = "mondoo-operator"
17+
}
18+
19+
variable "scanner_service_account" {
20+
description = "Kubernetes ServiceAccount name used by the scanner."
21+
type = string
22+
default = "mondoo-client-wif-target"
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
terraform {
2+
required_version = ">= 1.3"
3+
4+
required_providers {
5+
azurerm = {
6+
source = "hashicorp/azurerm"
7+
version = "~> 3.0"
8+
}
9+
azuread = {
10+
source = "hashicorp/azuread"
11+
version = "~> 2.47"
12+
}
13+
random = {
14+
source = "hashicorp/random"
15+
version = "~> 3.5"
16+
}
17+
}
18+
}
19+
20+
provider "azurerm" {
21+
features {}
22+
}
23+
24+
provider "azuread" {}

0 commit comments

Comments
 (0)