Skip to main content
Source

This page is generated from skills/eks-upgrade-check/references/deprecated-apis.md. Edit the source, not this page.

Vendored skill

This skill is sourced from eks-upgrade-check, also maintained by the APEX team.

Deprecated API Detection

Purpose

Scan live cluster resources for usage of deprecated or removed Kubernetes APIs that will break during or after the upgrade.

How to Check

Step 1: Get EKS Upgrade Insights

Use the EKS Insights API with category UPGRADE_READINESS — this is the most reliable source for deprecated API detection as AWS scans the audit logs.

  1. Get EKS Insights → filter for UPGRADE_READINESS
  2. For any non-PASSING insights → get detailed description
  3. Record: insight status, affected resources, recommended action

Step 2: Scan Live Resources

Run two scans in parallel for each resource type. Both are required because they catch different failure modes.

Resource types to scan:

  • Deployments, DaemonSets, StatefulSets, ReplicaSets
  • CronJobs, Jobs
  • Ingresses
  • NetworkPolicies
  • PodDisruptionBudgets
  • HorizontalPodAutoscalers
  • CustomResourceDefinitions
  • ValidatingWebhookConfigurations, MutatingWebhookConfigurations
  • FlowSchemas, PriorityLevelConfigurations

Step 2a: Object apiVersion scan

For each resource type, list resources and check the live apiVersion field against the deprecation table in Step 3. This is a detection step only: it surfaces candidate API paths from the live object apiVersion; it does not decide whether a resource needs migrating. That decision is made in Step 3b by writer identity — not by a served-vs-stored apiVersion comparison.

Step 2b: managedFields apiVersion scan

For each resource, inspect every entry in metadata.managedFields[] and check its apiVersion against the deprecation table in Step 3. The API server may auto-convert resources to the storage version, so Step 2a alone misses manifests originally applied under a deprecated apiVersion. managedFields preserves the apiVersion used by every writer (kubectl, controllers, Argo CD, Flux, Helm), so this scan covers all configuration sources.

kubectl get <kind> --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{"\t"}{range .metadata.managedFields[*]}{.manager}{"="}{.apiVersion}{","}{end}{"\n"}{end}'

Output is namespace/name<TAB>manager1=apiVersion1,manager2=apiVersion2,.... The manager portion identifies which writer used each apiVersion (e.g., kubectl-client-side-apply, argocd-application-controller, controller names) — this points to where the source manifest needs to be updated.

Anti-pattern — do not pre-filter with naïve substring greps.

# WRONG — `v1` is a prefix of `v1beta3`, so `grep -v` strips both lines.
... | grep -v "flowcontrol.apiserver.k8s.io/v1"

A single resource often has multiple manager=apiVersion entries on the same line (e.g., a controller writing v1 plus the user writing v1beta3). Filter-then-decide pipelines drop the line entirely as soon as any benign apiVersion matches. Walk the full output line by line and check each manager=apiVersion pair against the deprecation table in Step 3 instead.

Anti-pattern — do not substitute -o yaml or -o json.

# WRONG — kubectl 1.21+ hides managedFields from -o yaml / -o json by default
# (kubectl added --show-managed-fields in 1.21 and omits managedFields unless that
# flag is set), so this scan returns false negatives.
kubectl get <kind> -A -o yaml | grep apiVersion

Use the -o jsonpath form above. It accesses managedFields directly and is not affected by the default-hide behavior.

Step 3: Check for Removed APIs by Target Version

TargetRemoved APIReplacement
1.22networking.k8s.io/v1beta1 Ingressnetworking.k8s.io/v1
1.22rbac.authorization.k8s.io/v1beta1rbac.authorization.k8s.io/v1
1.25policy/v1beta1 PodSecurityPolicyPod Security Standards
1.25policy/v1beta1 PodDisruptionBudgetpolicy/v1
1.25batch/v1beta1 CronJobbatch/v1
1.25discovery.k8s.io/v1beta1 EndpointSlicediscovery.k8s.io/v1
1.25autoscaling/v2beta1 HPAautoscaling/v2
1.26autoscaling/v2beta2 HPAautoscaling/v2
1.26flowcontrol.apiserver.k8s.io/v1beta1flowcontrol.apiserver.k8s.io/v1beta2
1.27storage.k8s.io/v1beta1 CSIStorageCapacitystorage.k8s.io/v1
1.29flowcontrol.apiserver.k8s.io/v1beta2flowcontrol.apiserver.k8s.io/v1
1.32flowcontrol.apiserver.k8s.io/v1beta3flowcontrol.apiserver.k8s.io/v1

EndpointSlice (1.25) and CSIStorageCapacity (1.27) are detected via the generic deprecated-API path — EKS Upgrade Insights (Step 1) plus the managedFields writer test (Step 2b / Step 3b) — not a dedicated per-kind resource read. A list on the v1 API of either kind tells you nothing about v1beta1 writers, so no separate discovery.k8s.io / storage.k8s.io scan or RBAC grant is required for them.

Target >= 1.33: Live Lookup Required for Removed APIs

The removal table above is current through Kubernetes 1.32 (as of 2026-08-05). It does NOT cover API removals in 1.33 or later. If the target version is >= 1.33 — and in particular for Target >= 1.34, which this table does not cover at all — you MUST perform a live lookup before reporting "no removed APIs found."

How to check:

  1. Search AWS docs: search_documentation for "EKS Kubernetes removed APIs"
  2. Search AWS docs: search_documentation for "Kubernetes deprecated API migration guide"
  3. Fetch the Kubernetes "Deprecated API Migration Guide" and the CHANGELOG for the target minor version (e.g., CHANGELOG-1.34.md) via read_documentation.
  4. Cross-check the EKS Upgrade Insights from Step 1 — AWS scans audit logs and flags removed-API usage per target version.

If no removed APIs are found after live lookup: Report "No removed APIs identified for based on available documentation (as of the check date)" and advise re-checking closer to the upgrade date, as documentation may be updated.

If live sources are unreachable: Report "Removed APIs for could not be verified — documentation unavailable" with MEDIUM severity. Do NOT assume none exist.

Step 3b: Filter Out Already-Migrated / System-Written Resources (deterministic rule)

A v1beta3 (or other removed-version) string appearing in metadata.managedFields[] does NOT by itself mean a resource needs migrating. The deciding question is who wrote the removed version — a resource is a real finding only if a user-controlled writer actually wrote it.

Why not compare served vs stored apiVersion? Reading the object's live apiVersion (e.g. kubectl get flowschema <name> -o jsonpath='{.apiVersion}') cannot distinguish a migrated object from an unmigrated one: the API server serves every object at the version you request, so the read-back value reflects the request, not the source manifest. On 1.30+ control planes a v1 read-back can hide a manifest still applied as v1beta3. Do NOT use served/stored apiVersion as a per-object signal.

Apply this deterministic test to every removed-API kind surfaced via managedFields in Step 2b (FlowSchema / PriorityLevelConfiguration and all other kinds alike — Ingress, PodDisruptionBudget, CronJob, HPA, EndpointSlice, etc.). The writer-identity filter is general: any kind can carry a stale removed-version entry written by an internal/system manager, so the same false-positive risk applies beyond APF. Objects surfaced only by Step 2a's live-object apiVersion that have no managedFields writer signal are validated separately (see the managedFields-absence caveat below):

Writer identity (the only per-object signal). For any removed-version entry in managedFields, check the manager (writer):

  • If the writer is a Kubernetes/EKS-internal APF controller — its name starts with api-priority-and-fairness-config- (e.g. api-priority-and-fairness-config-consumer-v1, -producer-v1) — or is the EKS-managed writer eks, or the internal control-plane writer eks-internalEXCLUDE. These are the API server's own bootstrap controllers and EKS-managed/control-plane fields; the user cannot and need not change them. AWS documents the eks writer string: both server-side-apply and client-side managed fields on EKS "are tagged with manager: eks" (kubernetes-field-management.html, "Field Management"). Internal control-plane writers such as eks-internal are likewise not user tools and do not count as a user-managed writer.
  • If the writer is a user toolkubectl-*, helm, argocd-application-controller, flux, or any other non-APF manager → COUNT it. This points to a real source manifest that must be updated.

Outcome: A resource counts as a deprecated-API finding only if a user tool wrote a removed version in managedFields. If the only removed-version trace comes from internal APF controllers → it is a false positive; exclude it and record it under Informational Findings as "system-written — no action required."

Caveat — spoofability: managedFields.manager is client-supplied and can be spoofed or renamed; treat writer identity as strong evidence, not proof. When a finding is surprising, confirm against the actual source manifests (GitOps repo, Helm values) before acting on it.

Caveat — managedFields absence: this caveat covers spoofed or renamed managers; it does NOT cover managedFields absence. Objects whose managedFields were stripped or never recorded (e.g., after a Velero/OADP restore or a managedFields-clearing webhook) carry no writer signal — exclude them from the Step 3b writer test and validate them separately against source manifests. Treating absent managedFields as "no user-tool writer" is a false-negative blind spot. If that separate check of the source manifests (GitOps repo, Helm values) confirms a user tool authored the removed version, treat the object as having a user-tool writer for counting purposes (count the path); if the manifests show no such authorship, leave it excluded.

An API path (e.g., flowschemas) is counted only if at least one object on that path has a user-tool writer of a removed version. If every object on the path is excluded, the path contributes 0 points — do NOT deduct for it, and do NOT describe it as a blocker.

Step 4: Classify Findings

For each deprecated API found, record the source (object from Step 2a / managedFields from Step 2b) and severity:

  • Removed in target version → HIGH severity, action required
  • Deprecated but still available in target → LOW severity, plan migration
  • Removed in future version → INFO, awareness only

If a single resource is flagged by both Step 2a and Step 2b, report it once with source: object+managedFields. Counting at the API-path level (not the resource level) is canonical — see references/report-generation.md Category 2.

Output Format

For each finding, report:

  • API version and kind
  • Resource name and namespace
  • Source (object / managedFields / object+managedFields)
  • Whether it's removed in the target version or just deprecated
  • Specific migration command (e.g., update apiVersion field, re-apply manifests with the new apiVersion)

Score Impact

Canonical scoring is defined in references/report-generation.md §Category 2 (Deprecated APIs).

FindingDeduction
API removed in target version5 pts per API path (max 20)
API deprecated but available1 pt per API path (sub-cap: max 5 pts — enforced in report-generation.md Category 2 pseudocode)