> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Prefer Using Dedicated AKS Service Accounts

### More Info:

Use dedicated service accounts integrated with Azure Active Directory so users and groups get scoped, auditable access to Kubernetes resources instead of shared or default identities.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS AKS

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Determine whether the AKS cluster is Azure AD–integrated**
           * Run on any machine with Azure CLI access:
             ```bash theme={null}
             az aks show \
               --resource-group <RESOURCE_GROUP_NAME> \
               --name <CLUSTER_NAME> \
               --query "{aadProfile:aadProfile, oidcIssuerProfile:oidcIssuerProfile}" \
               --output json
             ```
           * Review the output: confirm that either a legacy `aadProfile` is configured or `oidcIssuerProfile.enabled` is `true`. If neither is present, plan to enable Azure AD / OIDC integration as per your organization’s standards before proceeding.

        2. **Inventory who is accessing the cluster and how (AAD users/groups vs. shared identities)**
           * List Azure AD app registrations and service principals used for AKS access (run from any machine with Azure AD Graph/MS Graph permissions):
             ```bash theme={null}
             az ad sp list --filter "servicePrincipalType eq 'Application'" --query "[].{appId:appId, displayName:displayName}" --all -o table
             ```
           * Correlate these identities with:
             * The AKS cluster’s managed identity or service principals (`identity`, `servicePrincipalProfile` fields from `az aks show`).
             * Any identities configured in your IaC (ARM/Bicep/Terraform) for cluster access.
           * Decide which of these identities represent shared/broadly used accounts that should not have direct, unscoped access.

        3. **Review RBAC bindings to ensure use of scoped service accounts and AAD groups, not defaults/shared identities**
           * Get current role bindings and cluster role bindings (any machine with kubectl access):
             ```bash theme={null}
             kubectl get clusterrolebindings -o wide
             kubectl get rolebindings --all-namespaces -o wide
             ```
           * For more detail on subjects:
             ```bash theme={null}
             kubectl get clusterrolebindings -o yaml
             kubectl get rolebindings --all-namespaces -o yaml
             ```
           * Manually inspect subjects and look for:
             * `system:anonymous`, `system:unauthenticated`, or `system:masters`.
             * `system:serviceaccount:default:default` or other default service accounts used for application workloads.
             * Broad Azure AD groups or generic service principals instead of dedicated, least-privilege identities.
           * Decide which bindings should be refactored to:
             * Dedicated Kubernetes service accounts per application or component.
             * Azure AD groups mapped to appropriate ClusterRoles/Roles for human users.

        4. **Check whether workloads use dedicated Kubernetes service accounts vs. defaults**
           * List service accounts by namespace (any machine with kubectl access):
             ```bash theme={null}
             kubectl get sa --all-namespaces
             ```
           * Inspect pods to see which service accounts they use:
             ```bash theme={null}
             kubectl get pods --all-namespaces -o wide
             kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\n"}{end}'
             ```
           * Identify applications using `default` or other shared service accounts instead of dedicated ones. Decide which namespaces/workloads require creation and use of dedicated service accounts and corresponding Role/RoleBindings.

        5. **Define and implement the desired model for dedicated AKS service accounts and AAD integration (via console/CLI/IaC)**
           * Using Azure Portal, Azure CLI, or your IaC:
             * Ensure the cluster is configured to use Azure AD (managed AAD/OIDC) per step 1 if your policy requires it.
             * Create or update Azure AD groups to represent roles (e.g., `aks-dev-readonly`, `aks-ops-admin`) and assign the appropriate users.
             * In your Kubernetes manifests/IaC, define:
               * Dedicated `ServiceAccount` objects per application or CI/CD component.
               * `Role`/`ClusterRole` and `RoleBinding`/`ClusterRoleBinding` objects that bind those service accounts and AAD groups with least-privilege access.
           * This step is done through Azure Portal/CLI/IaC authoring; there is no single command that can generically “fix” the cluster because choices are organization- and workload-specific.

        6. **Verify that access is now via dedicated service accounts and AAD identities, and not via shared/default identities**
           * Re-run:
             ```bash theme={null}
             kubectl get clusterrolebindings -o yaml
             kubectl get rolebindings --all-namespaces -o yaml
             kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\n"}{end}'
             ```
           * Confirm that:
             * Human access is mediated through Azure AD users/groups mapped to appropriate RBAC roles.
             * Application workloads use named, dedicated service accounts instead of `default` or shared accounts.
             * Any previously identified broad/shared or default identities have been removed or re-scoped according to your access model.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl can’t configure Azure AD integration or how identities are mapped to AKS; those settings are managed in Azure (portal/CLI/IaC) at the managed control plane level. To address this finding, use Azure AD and AKS configuration as described in the Manual Steps section rather than attempting changes with kubectl.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Report AKS service account usage and AAD integration status
        # Run on: any machine with Azure CLI logged-in and kubectl context for the AKS cluster

        set -euo pipefail

        echo "=== 1. Cluster-level AAD integration status (managed control plane) ==="
        # Adjust --resource-group and --name as needed before running
        RG="MY-RESOURCE-GROUP"
        CLUSTER="MY-AKS-CLUSTER"

        echo "# az aks show --resource-group \"$RG\" --name \"$CLUSTER\" --query '{aadProfile:aadProfile,oidcIssuerProfile:oidcIssuerProfile,azureRbac:azurePortalFqdn}' -o json"
        az aks show \
          --resource-group "$RG" \
          --name "$CLUSTER" \
          --query '{aadProfile:aadProfile,oidcIssuerProfile:oidcIssuerProfile,azureRbac:azureRbacEnabled}' \
          -o json

        cat <<'EOF'

        Interpretation (AAD / OIDC):
        - aadProfile == null or disabled  => PROBLEM: cluster not integrated with Azure AD.
        - oidcIssuerProfile.enabled == false or null => PROBLEM: OIDC issuer not enabled (limits AAD-backed service account patterns).
        - azureRbacEnabled == false        => REVIEW: using native Kubernetes RBAC only (not Azure RBAC for Kubernetes).

        EOF

        echo "=== 2. List all service accounts and their usage across namespaces ==="
        echo "# Service accounts (namespaced) with basic metadata"
        kubectl get sa --all-namespaces -o custom-columns=\
        'NAMESPACE:.metadata.namespace,NAME:.metadata.name,SECRETS:.secrets[*].name' | sort

        cat <<'EOF'

        Interpretation (service accounts):
        - Heavy use of 'default' service account in many namespaces => PROBLEM: shared identity instead of dedicated SAs.
        - Few or no non-default SAs in app namespaces => PROBLEM: likely not using dedicated SAs per workload.
        EOF

        echo "=== 3. Pods using the 'default' service account (by namespace) ==="
        kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{";"}{.metadata.name}{";"}{.spec.serviceAccountName}{"\n"}{end}' \
          | awk -F';' 'NR==1 || $3=="default"' \
          | sort \
          | awk -F';' 'BEGIN{printf "%-25s %-40s %-25s\n","NAMESPACE","POD","SERVICEACCOUNT"} {printf "%-25s %-40s %-25s\n",$1,$2,$3}'

        cat <<'EOF'

        Interpretation:
        - Any application pod (non-system namespace) with SERVICEACCOUNT 'default' => PROBLEM:
          the pod is not bound to a dedicated, least-privilege service account.

        EOF

        echo "=== 4. Pods per service account (identify shared SAs) ==="
        kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{";"}{.metadata.name}{";"}{.spec.serviceAccountName}{"\n"}{end}' \
          | sort \
          | awk -F';' '{
              key=$1"/"$3;
              pods[key]=pods[key] ? pods[key]","$2 : $2;
              count[key]++;
            }
            END{
              printf "%-35s %-8s %s\n","NAMESPACE/SA","POD_CNT","PODS";
              for (k in pods) {
                printf "%-35s %-8d %s\n",k,count[k],pods[k];
              }
            }' | sort

        cat <<'EOF'

        Interpretation:
        - Service accounts shared by many unrelated applications within a namespace => REVIEW:
          may violate "dedicated per workload" intent; investigate if sharing is justified.

        EOF

        echo "=== 5. Service accounts annotated for projected tokens / OIDC (if any) ==="
        kubectl get sa --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{";"}{.metadata.name}{";"}{.metadata.annotations}{"\n"}{end}' \
          | grep -E 'azure.workload.identity|kubernetes.io/service-account\.issuer|azure\.com' || true

        cat <<'EOF'

        Interpretation:
        - Absence of annotations related to Azure Workload Identity / OIDC in app namespaces may
          indicate limited adoption of AAD-backed service account patterns (not a hard failure,
          but a gap if your target architecture expects Azure Workload Identity).

        EOF

        echo "=== 6. RBAC bindings that reference service accounts ==="
        echo "# ClusterRoleBindings with ServiceAccount subjects"
        kubectl get clusterrolebindings.rbac.authorization.k8s.io -o json \
          | jq -r '.items[]
              | select(.subjects != null)
              | . as $crb
              | .subjects[]
              | select(.kind=="ServiceAccount")
              | "\($crb.metadata.name);\(.namespace)//\(.name)"' 2>/dev/null \
          | awk -F';' 'BEGIN{printf "%-45s %-40s\n","CLUSTERROLEBINDING","SERVICEACCOUNT (ns/name)"} {printf "%-45s %-40s\n",$1,$2}' \
          | sort || true

        echo
        echo "# RoleBindings with ServiceAccount subjects (namespaced)"
        kubectl get rolebindings.rbac.authorization.k8s.io --all-namespaces -o json \
          | jq -r '.items[]
              | select(.subjects != null)
              | . as $rb
              | .subjects[]
              | select(.kind=="ServiceAccount")
              | "\($rb.metadata.namespace);\($rb.metadata.name);\(.namespace)//\(.name)"' 2>/dev/null \
          | awk -F';' 'BEGIN{printf "%-20s %-40s %-40s\n","NAMESPACE","ROLEBINDING","SERVICEACCOUNT (ns/name)"} {printf "%-20s %-40s %-40s\n",$1,$2,$3}' \
          | sort || true

        cat <<'EOF'

        Interpretation (RBAC):
        - Bindings that target 'default' service account in app namespaces with broad roles
          (e.g., cluster-admin, edit) => PROBLEM: shared, over-privileged identity.
        - Service accounts without any bindings might be unused (REVIEW: remove or repurpose).

        EOF

        echo "=== 7. Namespaces and default service account token mounting policy ==="
        kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{";"}{.metadata.annotations.pod-security\.kubernetes\.io/enforce}{";"}{.metadata.annotations."kubernetes.io/enforce-mount-token"}{"\n"}{end}' 2>/dev/null \
          | awk -F';' 'BEGIN{printf "%-25s %-15s %-25s\n","NAMESPACE","POD-SECURITY","ENFORCE-MOUNT-TOKEN"} {printf "%-25s %-15s %-25s\n",$1,$2,$3}' \
          | sort || true

        cat <<'EOF'

        Interpretation:
        - Namespaces where pods auto-mount tokens for 'default' SA and no explicit dedicated SAs
          are used => PROBLEM: broad, implicit access surface.

        EOF

        echo "=== REVIEW SUMMARY (what indicates a problem) ==="
        cat <<'EOF'
        Problem indicators to flag for manual review:
        1) Cluster not AAD-integrated or OIDC issuer disabled:
           - az aks show reports:
             - aadProfile == null or enableAzureRBAC/aadProfile.enabled == false
             - oidcIssuerProfile.enabled == false or null

        2) Workloads using shared or default identities:
           - Many pods (especially in app namespaces) show SERVICEACCOUNT 'default'.
           - Few or no non-default service accounts in those namespaces.
           - Single service account used by many different apps without clear justification.

        3) Overbroad RBAC on shared accounts:
           - RoleBinding / ClusterRoleBinding granting strong roles (admin, edit, cluster-admin)
             to:
               - default service account, or
               - service accounts used by multiple applications.

        4) Lack of AAD-backed patterns where required:
           - Organization standard requires Azure AD / Workload Identity, but:
               - Cluster AAD/OIDC disabled, and
               - No Azure Workload Identity / OIDC-related annotations on app service accounts.

        These findings do NOT auto-fix the issue; they provide evidence for a human review to
        decide on:
        - Enabling / tightening AAD and OIDC integration at the AKS control plane.
        - Creating dedicated per-workload service accounts and updating pod specs.
        - Adjusting RBAC so each service account has least-privilege access.
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
