, ,

Inventorying the TKGI Estate (TKGI to OpenShift Series, Part 5)

Before you design an OpenShift target you need an honest picture of what TKGI is actually running. Here is the read only discovery sweep I run across a three cluster estate, and the four things it turns up that nobody expected.

TKGI to OpenShift Series · Part 5 of 26

Our first pass at the estate counted 34 namespaces. Two weeks later the real number was 47, and the gap was not sloppiness, it was a discovery method that trusted the wrong command as its source of truth.

Key takeaways:
• Every command in this Part is read only. Nothing here mutates a running TKGI cluster.
• Your authoritative cluster list comes from BOSH, not from tkgi clusters. Headline command: bosh -e tkgi deployments | grep service-instance
• Capture 12 fields per namespace. Admission risk is the field that sets your migration wave order, not storage and not networking.
• On our three cluster estate, 99 of 312 running pods (32 percent) would be rejected by the OpenShift default restricted-v2 Security Context Constraint exactly as written.
• Budget 40 minutes of command time and two days of reconciliation. Reconciliation is the long pole.
Who this is for: You run Tanzu Kubernetes Grid Integrated Edition (TKGI, the platform formerly sold as Enterprise PKS) version 1.18 on NSX-T, with dev, staging and prod clusters, and you have settled on Red Hat OpenShift Container Platform 4 (OCP) on vSphere as the landing place. Nothing is installed on the target yet. If you are still weighing VMware Kubernetes Service instead, spend ten minutes on the TKGI to VKS guide before you commit, then come back.

What Discovery Has to Produce

Discovery on a migration is not an audit. An audit asks whether the platform is healthy. Discovery asks a narrower and more useful question: for each thing running on TKGI today, what has to change before it will start on OpenShift. Those are different outputs, and teams that run an audit first waste a fortnight producing a document nobody uses for planning.

A migration inventory has one job, which is to feed wave planning. Wave planning needs to know which namespaces are cheap to move, which are expensive, and which are blocked until somebody rebuilds a container image. You get that from twelve fields per namespace. Anything beyond twelve fields is a project that never finishes, and anything below eight leaves you guessing at cutover.

Below is the worksheet we run against every cluster. Copy it into a spreadsheet, one row per namespace, and populate it with the commands in the next section. This is the artifact you will come back to for the rest of the migration, and it is the single deliverable of this Part.

FieldWhy it decides something on OpenShiftSource
Cluster identity and BOSH deployment idProves you found every cluster, including ones nobody remembers creatingbosh deployments
Kubernetes server versionSets which API versions your manifests use and what the OCP API server will rejectkubectl version
Namespace list, excluding systemBecomes the Project list on OpenShift, one to one in most caseskubectl get ns
Workload kinds and replica countsDaemonSets and StatefulSets cost far more to move than Deploymentskubectl get all
Image reference and registry hostDecides what Harbor has to mirror and which pull secrets you recreatejq over pod spec
runAsUser, privileged, hostPath, hostNetworkSingle biggest predictor of a failed restore, because restricted-v2 rejects all four patternsjq over pod spec
Pod Security Admission enforce labelA namespace labelled privileged on TKGI is a namespace that will fail admission on OCPjq over namespace labels
PVC name, StorageClass, capacity, access modeDrives the StorageClass mapping table and the Velero restore plankubectl get pvc
Services of type LoadBalancerEach one is an NSX-T virtual server that has no automatic equivalent on OCPkubectl get svc
Ingress hosts, paths and TLS secretsBecomes Routes, and any annotation heavy Ingress needs a rewrite, not a restorekubectl get ingress
NetworkPolicy count per namespaceZero policies means your segmentation lives in the NSX-T distributed firewall and does not travelkubectl get netpol
Deprecated API requests observedTells you which manifests the newer OCP API server will refuse outrightapiserver metrics
TKGI estate inventory worksheet. Twelve fields per namespace, and every one of them changes a decision later.

Prerequisites and Preflight Checks

You need four credentials before you start, and it pays to prove all four in one go rather than discovering a missing one halfway through a cluster sweep. You need an Ops Manager (Tanzu Operations Manager, the appliance that installs and configures TKGI) login, a BOSH Director login, a TKGI API login whose UAA account carries the pks.clusters.admin scope, and cluster admin on each Kubernetes cluster. If your TKGI account only carries pks.clusters.manage, stop and get the admin scope. That single detail is what produced the 34 versus 47 gap in the opening paragraph.

Versions this Part was written and tested against: TKGI 1.18, BOSH CLI 7.5, Ops Manager CLI (om) 7.x, a kubectl 1.27 client against the TKGI supplied server, and on the target side OCP 4.20 with the matching oc client. Do not assume a TKGI patch level maps to a Kubernetes minor you remember, because it drifts across patches. Read the Product Snapshot in your own release notes and read kubectl version on the running cluster. On the OpenShift side this series prefers oc over kubectl throughout, for one reason worth stating once: oc understands Projects, Routes, ImageStreams and the SCC review subresources that kubectl cannot reach without raw API calls. On the TKGI side kubectl is correct and oc buys you nothing.

# Preflight. Prove every credential before you sweep anything. # Passwords come from the environment, never typed on the command line. $ read -rsp ‘TKGI password: ‘ TKGI_PASSWORD; export TKGI_PASSWORD $ tkgi login -a api.pks.corp.local -u pjha -p "$TKGI_PASSWORD" –ca-cert /etc/ssl/certs/opsman-ca.pem API Endpoint: api.pks.corp.local User: pjha Login successful. $ bosh -e tkgi env Name p-bosh UUID 3f0b1a2c-77d4-4c19-9d2e-8b41c6a5e0f7 Version 279.0.4 (00000000) CPI vsphere_cpi User director $ kubectl version –output=yaml | grep gitVersion gitVersion: v1.27.13 gitVersion: v1.27.13 # Confirm you can read cluster scoped objects. If this returns no, your kubeconfig # is namespace scoped and every sweep below will silently undercount. $ kubectl auth can-i list persistentvolumes –all-namespaces yes

If that last check prints no, fix it before going further. A namespace scoped kubeconfig does not fail loudly on most of the commands that follow. It quietly returns a subset, and a subset inventory is worse than no inventory because it looks finished.

flowchart TD
  A[Ops Manager tile] --> B[BOSH Director]
  B --> C[bosh deployments, authoritative list]
  C --> D[Reconcile against tkgi clusters]
  D --> E{Counts match}
  E -- no --> F[Chase orphan or foreign owned clusters]
  E -- yes --> G[Per cluster kubectl sweep]
  F --> G
  G --> H[Workload and image inventory]
  G --> I[Storage and PVC inventory]
  G --> J[Network and Ingress inventory]
  G --> K[Admission risk inventory]
  H --> L[Estate worksheet, one row per namespace]
  I --> L
  J --> L
  K --> L
  L --> M[Wave planning in Part 6]
Discovery flows from BOSH outward. Start at the TKGI API instead and you inherit whatever your UAA identity happens to see.

Estate Discovery, Step by Step

Six steps, roughly 40 minutes of command time on a three cluster estate. Run them in order. Steps 1 and 2 establish scope, and if you skip straight to step 3 you will sweep the wrong set of clusters very thoroughly.

1. Build an authoritative cluster list from BOSH

Every TKGI cluster is a BOSH deployment named service-instance_ followed by its UUID. BOSH does not care who created the cluster or which UAA scopes you hold, so this list is complete by construction. Start here.

$ bosh -e tkgi deployments | grep service-instance service-instance_2b1f9c40-6a7e-4f11-9c88-1d0e5b3a7c22 … pks-1.18.5 service-instance_7c4a1e83-2d55-4b90-a3f6-9e2b8d1c0455 … pks-1.18.5 service-instance_9f2d6b17-4e08-42a7-bb31-5c7a0f9e6d34 … pks-1.18.5 service-instance_c58e3a94-1b62-4d7c-8f05-2a9d4e6b1783 … pks-1.18.5 $ bosh -e tkgi deployments | grep -c service-instance 4 # Now ask the TKGI API the same question. $ tkgi clusters Name Plan Name UUID Status Action dev-01 small 2b1f9c40-6a7e-4f11-9c88-1d0e5b3a7c22 succeeded CREATE staging-01 medium 7c4a1e83-2d55-4b90-a3f6-9e2b8d1c0455 succeeded CREATE prod-01 large 9f2d6b17-4e08-42a7-bb31-5c7a0f9e6d34 succeeded UPDATE # Four deployments, three clusters. That delta is the whole point of step 1.

Four against three. On our estate the missing deployment was a cluster created two years earlier by a departed engineer under a different UAA account, still running, still consuming 12 vCPU and 48 GB of memory, and still serving an internal reporting tool that a finance team used every month. Nobody in the migration kickoff mentioned it because nobody in the room could see it.

Resolve the delta by asking BOSH for the deployment properties and reading the cluster name out of the manifest.

$ bosh -e tkgi -d service-instance_c58e3a94-1b62-4d7c-8f05-2a9d4e6b1783 vms Instance Process State IPs VM Type master/0d3a… running 10.44.12.31 medium worker/1f8c… running 10.44.12.44 medium worker/6b21… running 10.44.12.45 medium # Recover the human readable name from the TKGI API by UUID. $ tkgi cluster –uuid c58e3a94-1b62-4d7c-8f05-2a9d4e6b1783 Error: You do not have enough privileges to perform this action. Please contact the TKGI administrator. # Real failure. Fix by adding pks.clusters.admin to your UAA user, then retry. $ tkgi cluster –uuid c58e3a94-1b62-4d7c-8f05-2a9d4e6b1783 Name: reporting-legacy Plan Name: small Owner: svc-reporting Status: succeeded

2. Pull a kubeconfig per cluster into isolated files

Do not sweep four clusters through one kubeconfig with context switching. One stale context and you attribute prod namespaces to dev, which you will not notice until wave planning contradicts itself.

$ mkdir -p ~/t2os/kubeconfigs ~/t2os/inventory $ for c in dev-01 staging-01 prod-01 reporting-legacy; do KUBECONFIG=~/t2os/kubeconfigs/$c.yaml tkgi get-credentials $c done Fetching credentials for cluster dev-01. Context set for cluster dev-01. Fetching credentials for cluster staging-01. Context set for cluster staging-01. Fetching credentials for cluster prod-01. Context set for cluster prod-01. Fetching credentials for cluster reporting-legacy. Context set for cluster reporting-legacy.

3. Sweep namespaces and workloads

Exclude the system namespaces TKGI owns, because none of them migrate. On TKGI that means kube-system, kube-public, kube-node-lease, pks-system and vmware-system-csi. Everything else is a candidate Project on OpenShift.

$ export KUBECONFIG=~/t2os/kubeconfigs/prod-01.yaml $ SYS=’kube-system|kube-public|kube-node-lease|pks-system|vmware-system-csi’ $ kubectl get ns –no-headers -o custom-columns=NAME:.metadata.name | grep -Ev "$SYS" | wc -l 21 # Workload shape per namespace. StatefulSets and DaemonSets are the expensive rows. $ kubectl get deploy,sts,ds,cronjob -A –no-headers 2>/dev/null | grep -Ev "$SYS" | awk ‘{print $2}’ | cut -d/ -f1 | sort | uniq -c 58 deployment.apps 7 statefulset.apps 4 daemonset.apps 11 cronjob.batch $ kubectl get pods -A –field-selector=status.phase=Running –no-headers | grep -Evc "$SYS" 132

Those four DaemonSets deserve a note in the worksheet right now. A DaemonSet on TKGI is usually a log shipper or a monitoring agent mounting /var/log through a hostPath, and on OpenShift the platform already ships that function through cluster Operators. Most of them should be deleted rather than migrated, and deciding that early removes work from every later wave.

4. Inventory storage, then load balancing and Ingress

Storage first, because persistent volumes set the ceiling on how fast a wave can move. Every PVC row you capture becomes a line in the StorageClass mapping table that Part 9 builds and Part 19 executes.

$ kubectl get pvc -A -o json | jq -r ‘[.items[] | select(.metadata.namespace | test("^(kube-|pks-|vmware-)") | not)] | .[] | [.metadata.namespace, .metadata.name, .spec.storageClassName, .status.capacity.storage, (.spec.accessModes | join(","))] | @tsv’ payments pg-data-pg-0 vsphere-fast 400Gi ReadWriteOnce payments pg-data-pg-1 vsphere-fast 400Gi ReadWriteOnce reporting warehouse-vol vsphere-std 250Gi ReadWriteOnce shared artifacts-nfs nfs-legacy 120Gi ReadWriteMany … $ kubectl get pvc -A –no-headers | wc -l 22 # Total provisioned capacity, in GiB, across the estate. $ kubectl get pvc -A -o json | jq -r ‘[.items[].status.capacity.storage] | join(" ")’ | tr ‘ ‘ ‘n’ | sed ‘s/Gi//’ | paste -sd+ | bc 1843 # Anything not backed by vSphere CSI is a rebuild, not a restore. $ kubectl get sc –no-headers -o custom-columns=NAME:.metadata.name,PROV:.provisioner vsphere-fast csi.vsphere.vmware.com vsphere-std csi.vsphere.vmware.com nfs-legacy nfs.csi.k8s.io

That ReadWriteMany row on nfs-legacy is the one to circle. vSphere CSI on OpenShift does not offer ReadWriteMany block volumes, so either you keep an external NFS provider on the target or the application changes. Finding that in week one is worth more than any other single line in the inventory.

# Every LoadBalancer Service is an NSX-T virtual server that NCP created for you. # OpenShift will not recreate these. Each becomes a Route or an explicit decision. $ kubectl get svc -A –field-selector spec.type=LoadBalancer –no-headers | wc -l 9 $ kubectl get ingress -A –no-headers | wc -l 63 # Annotation heavy Ingress cannot be restored as is. Count the exposure. $ kubectl get ingress -A -o json | jq -r ‘[.items[] | select(.metadata.annotations // {} | keys | map(test("ncp/")) | any)] | length’ 17 # Segmentation reality check. Zero NetworkPolicy means the rules live in the NSX-T DFW. $ kubectl get netpol -A –no-headers | wc -l 0

Seventeen of 63 Ingress objects carried NCP specific annotations, and zero NetworkPolicy objects existed anywhere. Both numbers say the same thing. Network behaviour on this estate is not described in Kubernetes manifests, it lives in NSX-T, and nothing you back up with Velero will carry it across. If you want the background on how distributed firewall rules are actually expressed, the NSX Series treatment of DFW and security policy covers it properly and this series will not repeat it.

5. Score admission risk, the field that reorders your waves

Here is where most inventories go wrong. Operators reach for PodSecurityPolicy, because that is what TKGI used to enforce, and get an error they misread as a permissions problem.

$ kubectl get psp error: the server doesn’t have a resource type "psp" # Not a permissions problem. PodSecurityPolicy was removed upstream in Kubernetes 1.25, # and TKGI required migration to Pod Security Admission before 1.16. On 1.18 your # posture lives in namespace labels, so read those instead. $ kubectl get ns -o json | jq -r ‘.items[] | [.metadata.name, (.metadata.labels["pod-security.kubernetes.io/enforce"] // "unset")] | @tsv’ | grep -Ev ‘^(kube-|pks-|vmware-)’ payments privileged reporting privileged checkout unset search baseline shared privileged …

Three namespaces at privileged and a pile at unset, which on a cluster with no cluster wide default behaves as permissive. That is the inherited posture the series keeps warning about: TKGI let these pods run, so nobody ever fixed them. OpenShift will not. Its default Security Context Constraint, restricted-v2, drops all Linux capabilities and forces runAsUser into the range allocated to the Project by the openshift.io/sa.scc.uid-range annotation. Leave runAsUser empty and you are fine. Pin it to 0, or to any value outside that range, and the pod needs nonroot-v2 or anyuid, which somebody has to justify and grant.

So count the four patterns that restricted-v2 refuses, per namespace, and put the total in the worksheet.

$ kubectl get pods -A -o json | jq -r ‘ .items[] | select(.metadata.namespace | test("^(kube-|pks-|vmware-)") | not) | select( (.spec.securityContext.runAsUser? == 0) or (.spec.hostNetwork? == true) or (any(.spec.volumes[]?; .hostPath != null)) or (any(.spec.containers[]; .securityContext.privileged? == true)) or (any(.spec.containers[]; .securityContext.runAsUser? == 0)) ) | [.metadata.namespace, .metadata.name] | @tsv’ | tee ~/t2os/inventory/prod-01-scc-risk.tsv | wc -l 44 $ cut -f1 ~/t2os/inventory/prod-01-scc-risk.tsv | sort | uniq -c | sort -rn | head -5 14 payments 9 reporting 8 shared 7 search 6 checkout # Estate wide, after running the same sweep against dev-01 and staging-01: # dev-01 26 of 84, staging-01 29 of 96, prod-01 44 of 132. Total 99 of 312.

6. Catch deprecated API usage before the OCP API server does

Kubernetes exposes a counter for exactly this, and almost nobody reads it. It tells you which clients are still calling API versions that a newer server will refuse.

$ kubectl get –raw /metrics | grep apiserver_requested_deprecated_apis apiserver_requested_deprecated_apis{group="batch",removed_release="1.25",resource="cronjobs",subresource="",version="v1beta1"} 1 apiserver_requested_deprecated_apis{group="policy",removed_release="1.25",resource="poddisruptionbudgets",subresource="",version="v1beta1"} 1 apiserver_requested_deprecated_apis{group="autoscaling",removed_release="1.26",resource="horizontalpodautoscalers",subresource="",version="v2beta2"} 1 # Six distinct deprecated group and version pairs across the estate. Every one of them # is a manifest in somebody’s Git repository, not a cluster setting you can flip.
Production gotcha: Run step 5 against a running pod list, not against your Git manifests. Half the admission risk we found came from images that set USER 0 in their Dockerfile with no securityContext in the manifest at all. Reading the repository would have shown a clean spec and reported zero risk. Reading the cluster showed 99 pods. If a container image runs as root and the manifest is silent, TKGI ran it happily and OpenShift will not, and only the live cluster tells you.

Verification and What Green Looks Like

Discovery is finished when four numbers reconcile, not when the commands stop printing. Check these in order and do not move to wave planning until each one holds.

# 1. Cluster count from BOSH equals cluster count in your worksheet. $ bosh -e tkgi deployments | grep -c service-instance 4 $ awk -F, ‘NR>1 {print $1}’ ~/t2os/inventory/estate.csv | sort -u | wc -l 4 # 2. Namespace rows equal the sum of per cluster namespace counts. $ awk -F, ‘NR>1’ ~/t2os/inventory/estate.csv | wc -l 47 # 3. Every PVC has a StorageClass. A null here means a default class was assumed # at creation time, and that assumption does not survive a restore. $ kubectl get pvc -A -o json | jq -r ‘[.items[] | select(.spec.storageClassName == null)] | length’ 0 # 4. Every namespace row carries an admission risk score, including zeros. $ awk -F, ‘NR>1 && $7 == ""’ ~/t2os/inventory/estate.csv | wc -l 0

Green looks like this: 4 clusters, 47 namespaces, 312 running pods, 22 PVCs totalling 1,843 GiB, 9 LoadBalancer Services, 63 Ingress objects, 0 NetworkPolicy objects, 99 pods carrying admission risk, and 6 deprecated API group and version pairs. Nine numbers on one line. If you cannot state your estate in nine numbers, discovery is not done.

Admission risk by cluster Running pods versus pods that restricted-v2 would reject as written. Measured on a TKGI 1.18 estate, August 2026. Running pods Rejected by restricted-v2 dev-01 84 26 staging-01 96 29 prod-01 132 44 Estate total 99 of 312 running pods, 32 percent, need an SCC above restricted-v2 reporting-legacy excluded, scheduled for decommission rather than migration
Prod carries the highest absolute risk and the highest ratio. That inverts the usual advice to migrate dev first.

Backing Out of a Read Only Sweep

Rollback matters even here, and treating it as unnecessary is how a discovery exercise turns into an incident review. Nothing in this Part writes to a cluster, but three side effects are real and two of them have bitten us.

First, tkgi get-credentials writes credentials to disk. Four kubeconfigs, each with a bearer token valid for the lifetime of your UAA session, sitting in a home directory that is probably backed up somewhere. Clean them up when the sweep is done: shred -u ~/t2os/kubeconfigs/*.yaml. Second, the metrics endpoint in step 6 and the full pod list in step 5 both hit the API server hard on a large cluster. On our prod cluster the kubectl get pods -A -o json call returned 41 MB and pushed apiserver p99 latency from 180 ms to just over 2 seconds for about eleven seconds. Nothing failed, but if you run that during a deployment window somebody will page you. Run sweeps outside change windows and add --chunk-size=200 if the cluster is bigger than ours.

Third, if you did have to add pks.clusters.admin to your UAA account to complete step 1, remove it afterwards. An elevated scope granted for a two hour discovery task and never revoked is exactly the finding an auditor will pick up six months later, and you will not remember why it is there.

Common Discovery Failures and Fixes

What you seeCauseFix
tkgi clusters returns fewer rows than bosh deploymentsYour UAA account holds pks.clusters.manage, which shows only clusters you ownGrant pks.clusters.admin temporarily, resolve names by UUID, then revoke
error: the server doesn't have a resource type "psp"PodSecurityPolicy is gone. TKGI moved to Pod Security Admission before 1.16Read pod-security.kubernetes.io/enforce namespace labels instead
Error from server (Forbidden): persistentvolumes is forbiddenNamespace scoped kubeconfig from a non admin UAA identityRe-run tkgi get-credentials as a cluster admin and repeat the preflight auth can-i
jq filter returns nothing, exit code 0Optional field accessed without the ? suffix, so jq skipped the object silentlyUse .securityContext.runAsUser? and always sanity check the count against kubectl get pods -A | wc -l
apiserver_requested_deprecated_apis returns no linesCounter resets on API server restart, and a recent BOSH update wiped itRe-check 24 hours after the last control plane restart, and cross check with kubectl api-resources
PVC capacity totals disagree with vCenter datastore usageReleased PVs from deleted PVCs still hold VMDKs under the CSI retain policyList kubectl get pv in Released phase separately, they are decommission work not migration work
Failure to remediation lookup for TKGI estate discovery. Four of these six cost us a day each on the first pass.

Field Note from a Three Cluster Sweep

We planned discovery as a two day task and it took nine working days. Six of those extra days trace to one decision made in the first hour: we opened with tkgi clusters because that is the tool the platform gives you, wrote down three clusters, and built a wave plan around them.

The fourth cluster surfaced in week three, when a finance analyst raised a ticket asking whether the reporting tool would move too. Nobody on the migration team had heard of it. It had been created under a service account, it was running an unsupported TKGI plan, and it held a 250 GiB persistent volume with the only copy of three years of month end extracts. Rebuilding the wave plan around it cost four days, and the conversation with the data owner about whether that volume had a backup cost considerably more in goodwill than in hours.

Second lesson from the same sweep. We scored admission risk from the Git repositories because that felt rigorous and repeatable, and it reported 12 risky workloads. Running the same question against live pods returned 99. The difference was almost entirely images built from base layers that set USER 0 with no securityContext anywhere in the manifest. Had we carried the number 12 into wave planning, our first wave would have been sized at two weeks and would have run for six.

Verdict: use bosh deployments as your cluster list and treat tkgi clusters only as a name resolution helper. Score admission risk against running pods, never against manifests. And the ordering advice you will read everywhere, that you should migrate dev first because it is safest, is worth ignoring on an estate shaped like this one. Prod carried the highest admission risk ratio at 33 percent, so migrating dev first taught us the least about the problem we actually had. Migrate the noisiest namespace in the least critical cluster first, which for us was search in dev-01, because it exercised hostPath, a DaemonSet and a deprecated autoscaling API in one go.

Scope note: This inventory deliberately says nothing about BOSH release versions, stemcell ages or Ops Manager tile configuration. Those matter for keeping TKGI alive until cutover, and TKGI runs until October 2027 under Broadcom support, but they contribute nothing to what runs on OpenShift. Keep platform health tracking in your existing operations backlog and keep this worksheet focused on workloads.

Inventory First, Design Second

My recommendation for this Part is narrow and firm. Do not draw a single line of your OpenShift target architecture until the nine numbers in the verification section are on a page with your name on it. Every design decision waiting in Parts 6 through 11, how many clusters, which StorageClasses, how Projects map to old plans, how much of NSX-T behaviour has to be rebuilt as NetworkPolicy, is answered by a field in that worksheet. Designing first and discovering second means designing for the estate you imagine.

On Monday, run steps 1 and 2 against your own estate and nothing else. Twenty minutes. Compare the BOSH deployment count against what tkgi clusters shows you. If those two numbers disagree, you have just learned the most important thing this Part has to teach, and you have learned it about your own environment rather than mine.

Part 6 takes this worksheet and turns it into migration waves, using admission risk and storage footprint as the two sorting keys. If you want the wider map first, the TKGI to OpenShift guide lays out all 26 Parts and where this one sits.

Quick answers:
Can I automate this with a single script? Yes for steps 3 to 6, no for steps 1 and 2. Reconciling BOSH deployments against TKGI cluster names requires judgement about ownership that a script cannot supply.
Should I inventory system namespaces at all? Only to confirm what is in them. Nothing under kube-system, pks-system or vmware-system-csi migrates, because OpenShift supplies equivalents through cluster Operators.
How stale does this inventory get? On our estate, roughly 5 percent of rows changed per month. Re-run steps 3 and 5 at the start of every wave, not once at the beginning of the programme.
What if I decide on VKS instead? Steps 1 through 4 transfer unchanged. Step 5 does not, because VKS uses Pod Security Admission rather than Security Context Constraints, so the risk scoring question changes shape.
TKGI to OpenShift Series · Part 5 of 26
« Previous: Part 4  |  Guide  |  Next: Part 6 »

References

Broadcom TechDocs, Retrieving Cluster Credentials and Configuration. Behaviour of tkgi get-credentials, the KUBECONFIG environment variable, and single sign on flags.
Red Hat, Pod Admission and SCCs Version 2 in OpenShift. Why restricted-v2 drops all capabilities, how the openshift.io/sa.scc.uid-range annotation constrains runAsUser, and when a workload needs nonroot-v2 or anyuid.
Broadcom KB 446224, Incompatibility between TKGI and VCF 9.1 or NSX 9.1. October 2027 End of Support, and the removal of the legacy NSX Management Plane API that NCP depends on.

About The Author


Discover more from Journal of Intelligent Infrastructure

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure

Subscribe now to keep reading and get access to the full archive.

Continue reading