Our dev lead asked one question in the migration standup that stalled the room: with UAA gone, how do my engineers actually log in to the new cluster and push an image. That is what this part wires, the plumbing that turns an empty VKS cluster into one a team can use.
TL;DR · Key Takeaways
- VKS (vSphere Kubernetes Service) authenticates through vCenter Single Sign-On by default, using the VCF CLI. You do not rebuild UAA, and for most teams you should not federate an external identity provider on day one.
- vSphere Namespace permissions, Can edit, Can view and Owner, propagate into every cluster in that namespace as Kubernetes RBAC. Set them on the namespace, not per cluster.
- Owner is the only role that lets you create namespaces with kubectl, and it works for vCenter SSO users only, not for external OIDC users. Federating everything to mimic old LDAP quietly breaks that self-service.
- Harbor installs as a Supervisor Service from the vSphere Client, needs Contour or an NGINX load balancer, and the Supervisor trusts it automatically as long as the tlsSecretLabels stay set to managed-by vmware-vRegistry.
- Headline flow: kubectl vsphere login binds the vSphere identity, then a scoped RoleBinding grants each developer only what they need.
Two identity models, and which to standardize on
Last part we stood up dev-tkg-01 in the dev-apps namespace. It runs, but nobody except the platform admin can reach it, and it cannot pull a private image. Fixing that starts with a choice between two authentication models, and the choice matters more than it looks.
On TKGI, logins went through UAA (User Account and Authentication, the OAuth2 identity server that fronted Enterprise PKS) wired to your LDAP directory. VKS drops UAA entirely. By default it authenticates through vCenter Single Sign-On (SSO, the vSphere identity service), which already integrates with Active Directory and LDAP, and you drive it with the VCF CLI. The second model is an external OIDC (OpenID Connect) provider, federated into the Supervisor through Pinniped, the Kubernetes authentication service that runs a Supervisor component plus a Concierge component on each VKS cluster.
Two details about that federation matter in practice. That Concierge authorises requests inside each VKS cluster, so when you register an OIDC provider on the Supervisor, every existing and future cluster picks up the change with no per cluster step. Access changes also settle on the token lifetime rather than instantly, so revoking a group or rotating a role takes effect at the next login or refresh, not the moment you press enter. Plan cutovers and revocations around that lag instead of assuming an immediate switch.
Here is where the tutorial instinct goes wrong. Every quickstart shows you how to register an external OIDC provider, so migrating teams reach for it to recreate the UAA and LDAP experience. On this estate that was a mistake, because one capability silently disappears when a user comes in over OIDC instead of vCenter SSO, and it is the capability platform teams lean on most.
Speed is the minor point. This table is the one to keep, because it lays out the capability difference that actually drives the decision.
| Dimension | vCenter SSO (default) | External OIDC via Pinniped |
|---|---|---|
| How you log in | VCF CLI, no browser | Browser OIDC redirect |
| Backing directory | AD or LDAP through vCenter | Your own IdP |
| Owner role, namespace create by kubectl | Supported | Not supported |
| Best for | Platform and DevOps operators | Developer federation at scale |
Table 1. Identity model trade off. The Owner row is the one that reversed my first design.
Preflight before wiring identity and Harbor
Four things must be true before you start. You can authenticate to the Supervisor with vCenter SSO. The dev-apps namespace and its cluster exist and are healthy. You have the VCF CLI and the kubectl vSphere plugin on your workstation. And for Harbor you have vCenter 8.0a or higher, a load balancer or Contour ingress, an FQDN for the registry, and a storage policy already visible as a StorageClass. Each check below proves one of these.
What green looks like: the login returns a context, the cluster reports Provisioned, and a StorageClass backed by the vSphere CSI (Container Storage Interface) driver is present for Harbor to claim volumes from.
Map TKGI UAA groups to vSphere Namespace roles
On TKGI you handed out access with UAA scopes and LDAP groups, plus per cluster admin roles. VKS collapses most of that into vSphere Namespace permissions. You assign a role to an AD or LDAP group on the namespace, and the Supervisor propagates it into every cluster in that namespace as Kubernetes RBAC (role based access control). Assign Can edit to a group and the system writes a ClusterRoleBinding on each cluster automatically. You never touch the cluster to grant operator access, you touch the namespace.
This mapping table is the reference artifact for the whole identity cutover. I keep it open and tick each source group off as I assign its target role. Build yours from the groups your TKGI clusters authorize today, not from a generic template.
| TKGI source construct | VKS target | In-cluster effect |
|---|---|---|
| pks.clusters.admin scope | Namespace Owner role (vCenter SSO only) | Create and delete namespaces by kubectl |
| LDAP platform-ops group | Namespace Can edit | ClusterRoleBinding to edit on every cluster |
| LDAP read-only auditors | Namespace Can view | Read cluster objects, no in-cluster grant |
| App team developers | No namespace role | Scoped RoleBinding inside the app namespace |
| Harbor LDAP auth and robot logins | Harbor projects and robot accounts | Registry push and pull, covered below |
Table 2. UAA and LDAP to VKS access mapping. Notice developers get nothing at the namespace level, only a RoleBinding in their own space.
After you assign Can edit to the platform-ops group, confirm the propagation from the namespace. The binding names carry a wcp prefix and encode the group they came from, which makes them easy to audit.
flowchart TD A[AD or LDAP group] --> B[vSphere Namespace permission] B --> C[Supervisor propagates to clusters] C --> D[ClusterRoleBinding on each VKS cluster] D --> E[Scoped RoleBinding for app team] E --> F[kubectl vsphere login as the user]
Scope developer access with a RoleBinding
Developers should never inherit cluster-admin. On TKGI that separation was fuzzy; on VKS you make it explicit. As a cluster operator, bind the built-in edit ClusterRole to the developer group inside only their application namespace. Note the sso prefix on the subject name, because vCenter SSO identities carry it and the binding has to match exactly.
Get the prefix wrong and the developer authenticates but cannot deploy, which is a confusing failure because login works. Here is the exact error, and it is worth recognising on sight.
Read the exact subject name from the error or from kubectl auth whoami, correct the RoleBinding, and prove the grant without making the developer retry by hand.
Install Harbor as a Supervisor Service
Our source estate ran Harbor on its own VMs. On VKS you stop running registry VMs and install Harbor as a Supervisor Service from the vSphere Client, under Supervisor Management then Services. It needs a load balancer or an ingress controller, either an NGINX based load balancer or Contour, and since version 2.11.2 it can expose the registry through that load balancer directly. Download two files from support.broadcom.com, the harbor-service definition and the harbor-data-values configuration, upload the service definition to register the operator, then edit the data values and install.
Only a few properties matter on a first install, and one of them must not be touched. Set the hostname to your registry FQDN, point every storageClass at a real storage policy, and pull the admin password and secret key from your secret store rather than committing them. Leave the tlsSecretLabels set to managed-by vmware-vRegistry, because that label is what lets the Supervisor and VKS clusters trust the registry without an image pull secret.
Registry structure carries over more directly than identity does. On the old Harbor you kept a project per team or per environment, with robot accounts for CI. Recreate the same projects on the new Harbor, one per application namespace is a clean default, and issue a fresh robot account per pipeline rather than reusing old credentials. Keeping the project names identical means your image references change only in the registry hostname, which shrinks the edits teams make to their manifests during the workload waves later in this series.
Here is the failure that cost me an afternoon, and it hides in that storageClass line. I pasted our vCenter storage policy name verbatim, Harbor Storage Policy, capitals and spaces and all. The Harbor vSphere Pods came up but their PVCs sat Pending with no obvious cause until I described one.
A storage policy becomes a StorageClass by lower casing the name and replacing every space and underscore with a hyphen. Harbor Storage Policy becomes harbor-storage-policy. Correct the data values, reconfigure the service, and the PVCs bind. Once Harbor is running, read its external address and point DNS at it.
Verify access and image pulls, then roll back
End to end verification means three things succeed: a developer logs in and is scoped, a robot account pushes to Harbor, and a pod on the VKS cluster pulls that image with no image pull secret because the Supervisor trusts the registry. A 1.2 GB image that took about 90 seconds to pull cross site from the old registry lands in roughly 8 seconds from the in-datacenter Harbor, which is a nice side effect of moving the registry next to the clusters.
One check people skip is on the source side. After you prove a pull from the new Harbor, confirm the old registry and its LDAP auth still answer, because until every workload wave is done the source has to keep serving. A migration that quietly breaks the platform you are migrating away from is the worst kind, and repointing a shared DNS record too early is the easy way to cause it.
Rollback stays first class here because the source TKGI estate is still live. Revoking a person is a single delete, and their access stops at the next token refresh rather than instantly, so plan for a short overlap. Removing Harbor is a Manage Service action in the vSphere Client, and because the old Harbor keeps serving, application image pulls fall back to the source registry cleanly while you sort out the target.
| Error you see | Likely cause | Fix |
|---|---|---|
| User cannot create resource in namespace | RoleBinding subject name missing the sso prefix | Match the name from the error, re-apply the RoleBinding |
| Owner role not available for this user | User authenticated over external OIDC | Give namespace-create operators a vCenter SSO identity |
| PVC Pending, storageclass not found | Storage policy name has capitals or spaces | Lowercase it, replace spaces and underscores with hyphens |
| VKS pod ImagePullBackOff from Harbor | tlsSecretLabels changed from vmware-vRegistry | Restore the label, reconfigure the Harbor service |
| Harbor unreachable on its FQDN | DNS not pointed at the load balancer IP | Read EXTERNAL-IP from kubectl get svc, update DNS |
Table 3. Identity and registry failures and their remediation. Three of the five are a name that does not match, one on RBAC, one on storage, one on the trust label.
Field note and verdict
Wire identity before the first workload moves
A clean result looks like this: platform admins log in with vCenter SSO and can create namespaces, a Can edit group shows up as a ClusterRoleBinding in each cluster, developers hold only a scoped RoleBinding in their app namespace, Harbor answers on its FQDN over 443, a robot account docker login succeeds, and a VKS pod pulls a Harbor image with no imagePullSecret. Get there on dev-tkg-01 and you repeat the same pattern for staging and prod.
On your own estate on Monday, do one thing before you touch any OIDC config: list the AD and LDAP groups your TKGI clusters authorize today, then map each one to exactly one vSphere Namespace role using Table 2. That mapping on paper, made before you configure anything, is what keeps you out of the Owner role trap. For registry and cluster mechanics beyond this migration, the vSphere Kubernetes Service complete guide and the VCF 9 complete guide go deeper, and microsegmentation for these same namespaces sits in the NSX complete guide. Next part converts NSX-T distributed firewall rules to Antrea network policy on this cluster.
Questions worth answering
Do I have to run UAA anywhere on VKS?
No. vCenter SSO replaces it. Integrate your AD or LDAP into vCenter and you keep the same directory without the UAA component to operate.
Can developers use the Owner role over OIDC?
No. Owner is vCenter SSO only. Give OIDC developers a scoped RoleBinding, and keep an SSO-backed operator identity for creating namespaces.
Should Harbor use Contour or the NGINX load balancer?
Use Contour if you already run it as the Supervisor ingress, otherwise the NGINX load balancer is simpler. Set enableContourHttpProxy and enableNginxLoadBalancer as a matched pair, one true and one false.
Do I migrate all images now?
No. Stand up Harbor now and mirror a couple of images to prove pulls, but bulk image migration rides with each workload wave rather than one big push.
This series covers a production migration. Any step that touches identity or a registry should run in a change window against your own environment, with the source estate untouched until the target is verified.
References
- Configuring Identity and Access for VKS Clusters, Broadcom TechDocs
- Install Harbor as a Supervisor Service, Broadcom TechDocs
- Using RBAC Authorization, Kubernetes documentation


DrJha