A vSphere storage policy assigned to a vSphere Namespace yields two editions of a storage class available for use with persistent volumes. The edition you choose depends on your requirements.

Two Editions of a Storage Class Available for Use

When you assign a vSphere storage policy to a vSphere Namespace, the system creates a matching Kubernetes storage class in that vSphere Namespace. This storage class is replicated to each TKG cluster provisioned in that namespace. The storage class is then available to create persistent storage volumes for cluster workloads.

For each vSphere storage policy assigned to a vSphere Namespace, on Supervisor there is a single storage class with "Immediate" binding mode.
kubectl describe storageclass tkg-storage-policy
Name:                  tkg-storage-policy
IsDefaultClass:        No
Annotations:           cns.vmware.com/StoragePoolTypeHint=cns.vmware.com/vsan
Provisioner:           csi.vsphere.vmware.com
Parameters:            storagePolicyID=877b0f4b-b959-492a-b265-b4d460987b23
AllowVolumeExpansion:  True
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>
For each TKG cluster deployed in that vSphere Namespace, there are two storage classes: one that is the same as the corresponding storage class on Supervisor, and a second with *-latebinding appended to the name and "WaitForFirstConsumer" as the binding mode.
kubectl get sc
NAME                             PROVISIONER              RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
tkg-storage-policy               csi.vsphere.vmware.com   Delete          Immediate              true                   2m43s
tkg-storage-policy-latebinding   csi.vsphere.vmware.com   Delete          WaitForFirstConsumer   true                   2m43s
Use the latebinding edition of the storage class when you want to provision a persistent volume after the compute has been selected by the Kubernetes scheduler. For more information, see Volume Binding Mode in the Kubernetes documentation.
kubectl describe sc tkg-storage-policy
Name:                  tkg-storage-policy
IsDefaultClass:        No
Annotations:           <none>
Provisioner:           csi.vsphere.vmware.com
Parameters:            svStorageClass=tkg-storage-policy
AllowVolumeExpansion:  True
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>

kubectl describe sc tkg-storage-policy-latebinding
Name:                  tkg-storage-policy-latebinding
IsDefaultClass:        No
Annotations:           <none>
Provisioner:           csi.vsphere.vmware.com
Parameters:            svStorageClass=tkg-storage-policy
AllowVolumeExpansion:  True
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     WaitForFirstConsumer
Events:                <none>

Patching a Storage Class

For TKG on Supervisor, you cannot create a storage class manually using kubectl and YAML. You can only create a storage class using the vSphere storage policy framework and then apply it to a vSphere Namespace. The result is two corresponding storage classes on each TKG cluster provisioned in that vSphere Namespace.

While you cannot create a storage class manually using kubectl and YAML, you can modify an existing storage class using kubectl. Doing so may be required if you provisioned a TKG cluster without specifying a default storage class, and now you want to deploy an application using Helm or a Tanzu Package that requires a default storage class.

Instead of creating a entirely new cluster with default storage, you can patch the existing storage class and add the default = true annotation as described in the Kubernetes documentation: Change the default StorageClass.

For example, the following command returns two storage classes that are available on the TKG cluster:
kubectl describe sc
Name:                  gc-storage-profile
IsDefaultClass:        No
Annotations:           <none>
Provisioner:           csi.vsphere.vmware.com
Parameters:            svStorageClass=gc-storage-profile
AllowVolumeExpansion:  True
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>


Name:                  gc-storage-profile-latebinding
IsDefaultClass:        No
Annotations:           <none>
Provisioner:           csi.vsphere.vmware.com
Parameters:            svStorageClass=gc-storage-profile
AllowVolumeExpansion:  True
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     WaitForFirstConsumer
Events:                <none>
Use the following command to patch one of the storage classes and add the annotation:
kubectl patch storageclass gc-storage-profile -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
storageclass.storage.k8s.io/gc-storage-profile patched
Check the storage classes again and you see that one of them is patched so that it is now the default.
kubectl describe sc
Name:                  gc-storage-profile
IsDefaultClass:        Yes
Annotations:           storageclass.kubernetes.io/is-default-class=true
Provisioner:           csi.vsphere.vmware.com
Parameters:            svStorageClass=gc-storage-profile
AllowVolumeExpansion:  True
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>


Name:                  gc-storage-profile-latebinding
IsDefaultClass:        No
Annotations:           <none>
Provisioner:           csi.vsphere.vmware.com
Parameters:            svStorageClass=gc-storage-profile
AllowVolumeExpansion:  True
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     WaitForFirstConsumer
Events:                <none>