Repository: FoundationDB/fdb-kubernetes-operator
Pull request #1511 (2023-02-21 11:17:20)

Initial round of documenting the upgrade process
# Description

Fixes: https://github.com/FoundationDB/fdb-kubernetes-operator/issues/1473

## Type of change

*Please select one of the options below.*

- Documentation

## Discussion

-

## Testing

-

## Documentation

Updated.

## Follow-up

-

johscheuer: JFYI @xumengpanda 
foundationdb-ci: 
### Result of fdb-kubernetes-operator-pr on Linux CentOS 7

* Commit ID: d7c2b12d9471fa0931bd7ce4c826aa9086aa5127
* Duration 1:55:35
* Result: :white_check_mark: **SUCCEEDED**
* Error: `N/A`
* [Build Log](https://d1e0l78xbkh3xa.cloudfront.net/fdb-kubernetes-operator/fdb-kubernetes-operator-pr/54a17840-2204-48f6-8b80-648551ea4323.gz) terminal output (available for 30 days)
* [Build Workspace](https://d1e0l78xbkh3xa.cloudfront.net/fdb-kubernetes-operator-pr/54a17840-2204-48f6-8b80-648551ea4323/workspace.zip) zip file of the working directory (available for 30 days)

foundationdb-ci: 
### Result of fdb-kubernetes-operator-pr on Linux CentOS 7

* Commit ID: 39304d1367dea01b1ed6625867b02a69aa7bcd22
* Duration 1:42:45
* Result: :white_check_mark: **SUCCEEDED**
* Error: `N/A`
* [Build Log](https://d1e0l78xbkh3xa.cloudfront.net/fdb-kubernetes-operator/fdb-kubernetes-operator-pr/374d3dd2-9848-4579-bdd4-3b7063b580b1.gz) terminal output (available for 30 days)
* [Build Workspace](https://d1e0l78xbkh3xa.cloudfront.net/fdb-kubernetes-operator-pr/374d3dd2-9848-4579-bdd4-3b7063b580b1/workspace.zip) zip file of the working directory (available for 30 days)


Patch:
diff --git a/README.md b/README.md
index 527d1950..07027cdf 100644
--- a/README.md
+++ b/README.md
@@ -112,7 +112,7 @@ If you already have Docker installed you can use the following steps to build an
 
 ```bash
 # Create a container with amd64 which contains go
-docker run --rm --entrypoint=/bin/bash -ti --platform="linux/amd64" -v $(pwd):/work -w /work docker.io/library/golang:1.18 ./scripts/setup_container.sh
+docker run --rm --entrypoint=/bin/bash -ti --platform="linux/amd64" -v $(pwd):/work -w /work docker.io/library/golang:1.19 ./scripts/setup_container.sh
 # Now we can run the lint and test steps
 make fmt lint test
 ```
diff --git a/api/v1beta1/foundationdbcluster_types.go b/api/v1beta1/foundationdbcluster_types.go
index 921e63ae..38d4a3d3 100644
--- a/api/v1beta1/foundationdbcluster_types.go
+++ b/api/v1beta1/foundationdbcluster_types.go
@@ -490,7 +490,7 @@ type ProcessGroupStatus struct {
 	// This is only used within the reconciliation process, and should not be considered authoritative.
 	// Deprecated: Use ExclusionTimestamp instead.
 	Excluded bool `json:"excluded,omitempty"`
-	// ExcludedTimestamp defines when the process group has been fully excluded.
+	// ExclusionTimestamp defines when the process group has been fully excluded.
 	// This is only used within the reconciliation process, and should not be considered authoritative.
 	ExclusionTimestamp *metav1.Time `json:"exclusionTimestamp,omitempty"`
 	// ExclusionSkipped determines if exclusion has been skipped for a process, which will allow the process group to be removed without exclusion.
diff --git a/api/v1beta2/foundationdb_status.go b/api/v1beta2/foundationdb_status.go
index 4524b732..1e62d3b9 100644
--- a/api/v1beta2/foundationdb_status.go
+++ b/api/v1beta2/foundationdb_status.go
@@ -70,7 +70,7 @@ type FoundationDBStatusClusterInfo struct {
 
 	// Processes provides details on the processes that are reporting to the
 	// cluster.
-	Processes map[string]FoundationDBStatusProcessInfo `json:"processes,omitempty"`
+	Processes map[ProcessGroupID]FoundationDBStatusProcessInfo `json:"processes,omitempty"`
 
 	// Data provides information about the data in the database.
 	Data FoundationDBStatusDataStatistics `json:"data,omitempty"`
diff --git a/api/v1beta2/foundationdbcluster_types.go b/api/v1beta2/foundationdbcluster_types.go
index 01aa46a6..65b14f71 100644
--- a/api/v1beta2/foundationdbcluster_types.go
+++ b/api/v1beta2/foundationdbcluster_types.go
@@ -106,7 +106,7 @@ type FoundationDBClusterSpec struct {
 	// cluster. This list contains the process group IDs.
 	// +kubebuilder:validation:MinItems=0
 	// +kubebuilder:validation:MaxItems=500
-	ProcessGroupsToRemove []string `json:"processGroupsToRemove,omitempty"`
+	ProcessGroupsToRemove []ProcessGroupID `json:"processGroupsToRemove,omitempty"`
 
 	// ProcessGroupsToRemoveWithoutExclusion defines the process groups that we should
 	// remove from the cluster without excluding them. This list contains the
@@ -117,7 +117,7 @@ type FoundationDBClusterSpec struct {
 	// is fully replicated.
 	// +kubebuilder:validation:MinItems=0
 	// +kubebuilder:validation:MaxItems=500
-	ProcessGroupsToRemoveWithoutExclusion []string `json:"processGroupsToRemoveWithoutExclusion,omitempty"`
+	ProcessGroupsToRemoveWithoutExclusion []ProcessGroupID `json:"processGroupsToRemoveWithoutExclusion,omitempty"`
 
 	// ConfigMap allows customizing the config map the operator creates.
 	ConfigMap *corev1.ConfigMap `json:"configMap,omitempty"`
@@ -312,14 +312,14 @@ type LockSystemStatus struct {
 // ProcessGroupStatus represents the status of a ProcessGroup.
 type ProcessGroupStatus struct {
 	// ProcessGroupID represents the ID of the process group
-	ProcessGroupID string `json:"processGroupID,omitempty"`
+	ProcessGroupID ProcessGroupID `json:"processGroupID,omitempty"`
 	// ProcessClass represents the class the process group has.
 	ProcessClass ProcessClass `json:"processClass,omitempty"`
 	// Addresses represents the list of addresses the process group has been known to have.
 	Addresses []string `json:"addresses,omitempty"`
 	// RemoveTimestamp if not empty defines when the process group was marked for removal.
 	RemovalTimestamp *metav1.Time `json:"removalTimestamp,omitempty"`
-	// ExcludedTimestamp defines when the process group has been fully excluded.
+	// ExclusionTimestamp defines when the process group has been fully excluded.
 	// This is only used within the reconciliation process, and should not be considered authoritative.
 	ExclusionTimestamp *metav1.Time `json:"exclusionTimestamp,omitempty"`
 	// ExclusionSkipped determines if exclusion has been skipped for a process, which will allow the process group to be removed without exclusion.
@@ -328,6 +328,10 @@ type ProcessGroupStatus struct {
 	ProcessGroupConditions []*ProcessGroupCondition `json:"processGroupConditions,omitempty"`
 }
 
+// ProcessGroupID represents the ID of the process group
+// +kubebuilder:validation:MaxLength=63
+type ProcessGroupID string
+
 // GetExclusionString returns the exclusion string
 func (processGroupStatus *ProcessGroupStatus) GetExclusionString() string {
 	return fmt.Sprintf("locality_instance_id:%s", processGroupStatus.ProcessGroupID)
@@ -432,7 +436,7 @@ func (processGroupStatus *ProcessGroupStatus) AllAddressesExcluded(remainingMap
 }
 
 // NewProcessGroupStatus returns a new GroupStatus for the given processGroupID and processClass.
-func NewProcessGroupStatus(processGroupID string, processClass ProcessClass, addresses []string) *ProcessGroupStatus {
+func NewProcessGroupStatus(processGroupID ProcessGroupID, processClass ProcessClass, addresses []string) *ProcessGroupStatus {
 	return &ProcessGroupStatus{
 		ProcessGroupID: processGroupID,
 		ProcessClass:   processClass,
@@ -448,7 +452,7 @@ func NewProcessGroupStatus(processGroupID string, processClass ProcessClass, add
 }
 
 // FindProcessGroupByID finds a process group status for a given processGroupID.
-func FindProcessGroupByID(processGroups []*ProcessGroupStatus, processGroupID string) *ProcessGroupStatus {
+func FindProcessGroupByID(processGroups []*ProcessGroupStatus, processGroupID ProcessGroupID) *ProcessGroupStatus {
 	for _, processGroup := range processGroups {
 		if processGroup.ProcessGroupID == processGroupID {
 			return processGroup
@@ -459,12 +463,12 @@ func FindProcessGroupByID(processGroups []*ProcessGroupStatus, processGroupID st
 }
 
 // ContainsProcessGroupID evaluates if the ProcessGroupStatus contains a given processGroupID.
-func ContainsProcessGroupID(processGroups []*ProcessGroupStatus, processGroupID string) bool {
+func ContainsProcessGroupID(processGroups []*ProcessGroupStatus, processGroupID ProcessGroupID) bool {
 	return FindProcessGroupByID(processGroups, processGroupID) != nil
 }
 
 // MarkProcessGroupForRemoval sets the remove flag for the given process and ensures that the address is added.
-func MarkProcessGroupForRemoval(processGroups []*ProcessGroupStatus, processGroupID string, processClass ProcessClass, address string) (bool, *ProcessGroupStatus) {
+func MarkProcessGroupForRemoval(processGroups []*ProcessGroupStatus, processGroupID ProcessGroupID, processClass ProcessClass, address string) (bool, *ProcessGroupStatus) {
 	for _, processGroup := range processGroups {
 		if processGroup.ProcessGroupID != processGroupID {
 			continue
@@ -504,7 +508,7 @@ func MarkProcessGroupForRemoval(processGroups []*ProcessGroupStatus, processGrou
 // UpdateCondition will add or remove a condition in the ProcessGroupStatus.
 // If the old ProcessGroupStatus already contains the condition, and the condition is being set,
 // the condition is reused to contain the same timestamp.
-func (processGroupStatus *ProcessGroupStatus) UpdateCondition(conditionType ProcessGroupConditionType, set bool, oldProcessGroups []*ProcessGroupStatus, processGroupID string) {
+func (processGroupStatus *ProcessGroupStatus) UpdateCondition(conditionType ProcessGroupConditionType, set bool, oldProcessGroups []*ProcessGroupStatus, processGroupID ProcessGroupID) {
 	if set {
 		processGroupStatus.addCondition(oldProcessGroups, processGroupID, conditionType)
 	} else {
@@ -514,7 +518,7 @@ func (processGroupStatus *ProcessGroupStatus) UpdateCondition(conditionType Proc
 
 // addCondition will add the condition to the ProcessGroupStatus.
 // If the old ProcessGroupStatus already contains the condition the condition is reused to contain the same timestamp.
-func (processGroupStatus *ProcessGroupStatus) addCondition(oldProcessGroups []*ProcessGroupStatus, processGroupID string, conditionType ProcessGroupConditionType) {
+func (processGroupStatus *ProcessGroupStatus) addCondition(oldProcessGroups []*ProcessGroupStatus, processGroupID ProcessGroupID, conditionType ProcessGroupConditionType) {
 	var oldProcessGroupStatus *ProcessGroupStatus
 
 	// Check if we got a ProcessGroupStatus for the processGroupID
@@ -575,7 +579,7 @@ func CreateProcessCountsFromProcessGroupStatus(processGroupStatus []*ProcessGrou
 }
 
 // FilterByCondition returns a string slice of all ProcessGroupIDs that contains a condition with the given type.
-func FilterByCondition(processGroupStatus []*ProcessGroupStatus, conditionType ProcessGroupConditionType, ignoreRemoved bool) []string {
+func FilterByCondition(processGroupStatus []*ProcessGroupStatus, conditionType ProcessGroupConditionType, ignoreRemoved bool) []ProcessGroupID {
 	return FilterByConditions(processGroupStatus, map[ProcessGroupConditionType]bool{conditionType: true}, ignoreRemoved)
 }
 
@@ -586,8 +590,8 @@ func FilterByCondition(processGroupStatus []*ProcessGroupStatus, conditionType P
 // groups with that condition will be returned. If a condition is mapped to
 // false in the conditionRules map, only process groups without that condition
 // will be returned.
-func FilterByConditions(processGroupStatus []*ProcessGroupStatus, conditionRules map[ProcessGroupConditionType]bool, ignoreRemoved bool) []string {
-	result := make([]string, 0, len(processGroupStatus))
+func FilterByConditions(processGroupStatus []*ProcessGroupStatus, conditionRules map[ProcessGroupConditionType]bool, ignoreRemoved bool) []ProcessGroupID {
+	result := make([]ProcessGroupID, 0, len(processGroupStatus))
 
 	for _, groupStatus := range processGroupStatus {
 		if ignoreRemoved && groupStatus.IsMarkedForRemoval() {
@@ -1562,7 +1566,7 @@ func (cluster *FoundationDBCluster) VersionCompatibleUpgradeInProgress() bool {
 }
 
 // ProcessGroupIsBeingRemoved determines if an instance is pending removal.
-func (cluster *FoundationDBCluster) ProcessGroupIsBeingRemoved(processGroupID string) bool {
+func (cluster *FoundationDBCluster) ProcessGroupIsBeingRemoved(processGroupID ProcessGroupID) bool {
 	if processGroupID == "" {
 		return false
 	}
@@ -1716,18 +1720,18 @@ type CrashLoopContainerObject struct {
 	ContainerName string `json:"containerName,omitempty"`
 
 	// Target processes to kill inside the container.
-	Targets []string `json:"targets,omitempty"`
+	Targets []ProcessGroupID `json:"targets,omitempty"`
 }
 
 // BuggifyConfig provides options for injecting faults into a cluster for testing.
 type BuggifyConfig struct {
 	// NoSchedule defines a list of process group IDs that should fail to schedule.
-	NoSchedule []string `json:"noSchedule,omitempty"`
+	NoSchedule []ProcessGroupID `json:"noSchedule,omitempty"`
 
 	// CrashLoops defines a list of process group IDs that should be put into a
 	// crash looping state.
 	// Deprecated: use CrashLoopContainers instead.
-	CrashLoop []string `json:"crashLoop,omitempty"`
+	CrashLoop []ProcessGroupID `json:"crashLoop,omitempty"`
 
 	// CrashLoopContainers defines a list of process group IDs and containers
 	// that should be put into a crash looping state.
@@ -1741,7 +1745,7 @@ type BuggifyConfig struct {
 	// restart command. This can be useful to simulate cases where the kill command is not restarting all
 	// processes. IgnoreDuringRestart does not support the wildcard option to ignore all of this specific cluster processes.
 	// +kubebuilder:validation:MaxItems=1000
-	IgnoreDuringRestart []string `json:"ignoreDuringRestart,omitempty"`
+	IgnoreDuringRestart []ProcessGroupID `json:"ignoreDuringRestart,omitempty"`
 }
 
 // LabelConfig allows customizing labels used by the operator.
@@ -2099,8 +2103,8 @@ func (cluster *FoundationDBCluster) GetIgnoreTerminatingPodsSeconds() int {
 
 // AddProcessGroupsToRemovalList adds the provided process group IDs to the remove list.
 // If a process group ID is already present on that list it won't be added a second time.
-func (cluster *FoundationDBCluster) AddProcessGroupsToRemovalList(processGroupIDs []string) {
-	removals := map[string]None{}
+func (cluster *FoundationDBCluster) AddProcessGroupsToRemovalList(processGroupIDs []ProcessGroupID) {
+	removals := map[ProcessGroupID]None{}
 
 	for _, id := range cluster.Spec.ProcessGroupsToRemove {
 		removals[id] = None{}
@@ -2117,8 +2121,8 @@ func (cluster *FoundationDBCluster) AddProcessGroupsToRemovalList(processGroupID
 
 // AddProcessGroupsToNoScheduleList adds the provided process group IDs to the no-schedule list.
 // If a process group ID is already present on that list it won't be added a second time.
-func (cluster *FoundationDBCluster) AddProcessGroupsToNoScheduleList(processGroupIDs []string) {
-	noScheduleProcesses := map[string]None{}
+func (cluster *FoundationDBCluster) AddProcessGroupsToNoScheduleList(processGroupIDs []ProcessGroupID) {
+	noScheduleProcesses := map[ProcessGroupID]None{}
 
 	for _, id := range cluster.Spec.Buggify.NoSchedule {
 		noScheduleProcesses[id] = None{}
@@ -2134,8 +2138,8 @@ func (cluster *FoundationDBCluster) AddProcessGroupsToNoScheduleList(processGrou
 }
 
 // RemoveProcessGroupsFromNoScheduleList removes the provided process group IDs from the no-schedule list.
-func (cluster *FoundationDBCluster) RemoveProcessGroupsFromNoScheduleList(processGroupIDs []string) {
-	processGroupIDsToRemove := make(map[string]None)
+func (cluster *FoundationDBCluster) RemoveProcessGroupsFromNoScheduleList(processGroupIDs []ProcessGroupID) {
+	processGroupIDsToRemove := make(map[ProcessGroupID]None)
 	for _, processGroupID := range processGroupIDs {
 		processGroupIDsToRemove[processGroupID] = None{}
 	}
@@ -2155,7 +2159,7 @@ func (cluster *FoundationDBCluster) RemoveProcessGroupsFromNoScheduleList(proces
 // AddProcessGroupsToCrashLoopList adds the provided process group IDs to the crash-loop list.
 // If a process group ID is already present on that list or all the processes are set into crash-loop
 // it won't be added a second time.
-func (cluster *FoundationDBCluster) AddProcessGroupsToCrashLoopList(processGroupIDs []string) {
+func (cluster *FoundationDBCluster) AddProcessGroupsToCrashLoopList(processGroupIDs []ProcessGroupID) {
 	crashLoop, _ := cluster.GetCrashLoopProcessGroups()
 
 	for _, processGroupID := range processGroupIDs {
@@ -2168,8 +2172,8 @@ func (cluster *FoundationDBCluster) AddProcessGroupsToCrashLoopList(processGroup
 }
 
 // RemoveProcessGroupsFromCrashLoopList removes the provided process group IDs from the crash-loop list.
-func (cluster *FoundationDBCluster) RemoveProcessGroupsFromCrashLoopList(processGroupIDs []string) {
-	processGroupIDsToRemove := make(map[string]None)
+func (cluster *FoundationDBCluster) RemoveProcessGroupsFromCrashLoopList(processGroupIDs []ProcessGroupID) {
+	processGroupIDsToRemove := make(map[ProcessGroupID]None)
 	for _, processGroupID := range processGroupIDs {
 		processGroupIDsToRemove[processGroupID] = None{}
 	}
@@ -2187,8 +2191,8 @@ func (cluster *FoundationDBCluster) RemoveProcessGroupsFromCrashLoopList(process
 
 // AddProcessGroupsToRemovalWithoutExclusionList adds the provided process group IDs to the remove without exclusion list.
 // If a process group ID is already present on that list it won't be added a second time.
-func (cluster *FoundationDBCluster) AddProcessGroupsToRemovalWithoutExclusionList(processGroupIDs []string) {
-	removals := map[string]None{}
+func (cluster *FoundationDBCluster) AddProcessGroupsToRemovalWithoutExclusionList(processGroupIDs []ProcessGroupID) {
+	removals := map[ProcessGroupID]None{}
 
 	for _, id := range cluster.Spec.ProcessGroupsToRemoveWithoutExclusion {
 		removals[id] = None{}
@@ -2218,8 +2222,8 @@ func (cluster *FoundationDBCluster) GetRunningVersion() string {
 
 // GetCrashLoopProcessGroups returns the process group IDs that are marked for crash looping. The second return value indicates
 // if all process group IDs in a cluster should be crash looping.
-func (cluster *FoundationDBCluster) GetCrashLoopProcessGroups() (map[string]None, bool) {
-	crashLoopPods := make(map[string]None, len(cluster.Spec.Buggify.CrashLoop))
+func (cluster *FoundationDBCluster) GetCrashLoopProcessGroups() (map[ProcessGroupID]None, bool) {
+	crashLoopPods := make(map[ProcessGroupID]None, len(cluster.Spec.Buggify.CrashLoop))
 	crashLoopAll := false
 	for _, processGroupID := range cluster.Spec.Buggify.CrashLoop {
 		if processGroupID == "*" {
@@ -2233,11 +2237,11 @@ func (cluster *FoundationDBCluster) GetCrashLoopProcessGroups() (map[string]None
 
 // GetCrashLoopContainerProcessGroups returns the process group IDs in containers that are marked for crash looping.
 // Returns map[ContainerName](map[ProcessGroupID]None).
-func (cluster *FoundationDBCluster) GetCrashLoopContainerProcessGroups() map[string](map[string]None) {
-	crashLoopTargets := make(map[string](map[string]None))
+func (cluster *FoundationDBCluster) GetCrashLoopContainerProcessGroups() map[string]map[ProcessGroupID]None {
+	crashLoopTargets := make(map[string]map[ProcessGroupID]None)
 	for _, target := range cluster.Spec.Buggify.CrashLoopContainers {
 		if _, ok := crashLoopTargets[target.ContainerName]; !ok {
-			crashLoopTargets[target.ContainerName] = make(map[string]None)
+			crashLoopTargets[target.ContainerName] = make(map[ProcessGroupID]None)
 		}
 
 		for _, processID := range target.Targets {
diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go
index f32045db..3e6b8ed0 100644
--- a/api/v1beta2/zz_generated.deepcopy.go
+++ b/api/v1beta2/zz_generated.deepcopy.go
@@ -98,12 +98,12 @@ func (in *BuggifyConfig) DeepCopyInto(out *BuggifyConfig) {
 	*out = *in
 	if in.NoSchedule != nil {
 		in, out := &in.NoSchedule, &out.NoSchedule
-		*out = make([]string, len(*in))
+		*out = make([]ProcessGroupID, len(*in))
 		copy(*out, *in)
 	}
 	if in.CrashLoop != nil {
 		in, out := &in.CrashLoop, &out.CrashLoop
-		*out = make([]string, len(*in))
+		*out = make([]ProcessGroupID, len(*in))
 		copy(*out, *in)
 	}
 	if in.CrashLoopContainers != nil {
@@ -115,7 +115,7 @@ func (in *BuggifyConfig) DeepCopyInto(out *BuggifyConfig) {
 	}
 	if in.IgnoreDuringRestart != nil {
 		in, out := &in.IgnoreDuringRestart, &out.IgnoreDuringRestart
-		*out = make([]string, len(*in))
+		*out = make([]ProcessGroupID, len(*in))
 		copy(*out, *in)
 	}
 }
@@ -230,7 +230,7 @@ func (in *CrashLoopContainerObject) DeepCopyInto(out *CrashLoopContainerObject)
 	*out = *in
 	if in.Targets != nil {
 		in, out := &in.Targets, &out.Targets
-		*out = make([]string, len(*in))
+		*out = make([]ProcessGroupID, len(*in))
 		copy(*out, *in)
 	}
 }
@@ -623,12 +623,12 @@ func (in *FoundationDBClusterSpec) DeepCopyInto(out *FoundationDBClusterSpec) {
 	out.FaultDomain = in.FaultDomain
 	if in.ProcessGroupsToRemove != nil {
 		in, out := &in.ProcessGroupsToRemove, &out.ProcessGroupsToRemove
-		*out = make([]string, len(*in))
+		*out = make([]ProcessGroupID, len(*in))
 		copy(*out, *in)
 	}
 	if in.ProcessGroupsToRemoveWithoutExclusion != nil {
 		in, out := &in.ProcessGroupsToRemoveWithoutExclusion, &out.ProcessGroupsToRemoveWithoutExclusion
-		*out = make([]string, len(*in))
+		*out = make([]ProcessGroupID, len(*in))
 		copy(*out, *in)
 	}
 	if in.ConfigMap != nil {
@@ -993,7 +993,7 @@ func (in *FoundationDBStatusClusterInfo) DeepCopyInto(out *FoundationDBStatusClu
 	in.DatabaseConfiguration.DeepCopyInto(&out.DatabaseConfiguration)
 	if in.Processes != nil {
 		in, out := &in.Processes, &out.Processes
-		*out = make(map[string]FoundationDBStatusProcessInfo, len(*in))
+		*out = make(map[ProcessGroupID]FoundationDBStatusProcessInfo, len(*in))
 		for key, val := range *in {
 			(*out)[key] = *val.DeepCopy()
 		}
diff --git a/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml b/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
index 8294c2f3..feb5b140 100644
--- a/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
+++ b/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
@@ -9933,6 +9933,7 @@ spec:
                 properties:
                   crashLoop:
                     items:
+                      maxLength: 63
                       type: string
                     type: array
                   crashLoopContainers:
@@ -9942,6 +9943,7 @@ spec:
                           type: string
                         targets:
                           items:
+                            maxLength: 63
                             type: string
                           type: array
                       type: object
@@ -9950,11 +9952,13 @@ spec:
                     type: boolean
                   ignoreDuringRestart:
                     items:
+                      maxLength: 63
                       type: string
                     maxItems: 1000
                     type: array
                   noSchedule:
                     items:
+                      maxLength: 63
                       type: string
                     type: array
                 type: object
@@ -10243,12 +10247,14 @@ spec:
                 type: string
               processGroupsToRemove:
                 items:
+                  maxLength: 63
                   type: string
                 maxItems: 500
                 minItems: 0
                 type: array
               processGroupsToRemoveWithoutExclusion:
                 items:
+                  maxLength: 63
                   type: string
                 maxItems: 500
                 minItems: 0
@@ -13828,6 +13834,7 @@ spec:
                         type: object
                       type: array
                     processGroupID:
+                      maxLength: 63
                       type: string
                     removalTimestamp:
                       format: date-time
diff --git a/controllers/bounce_processes.go b/controllers/bounce_processes.go
index 144952b4..fc316d46 100644
--- a/controllers/bounce_processes.go
+++ b/controllers/bounce_processes.go
@@ -120,7 +120,7 @@ func (bounceProcesses) reconcile(ctx context.Context, r *FoundationDBClusterReco
 	upgrading := cluster.Status.RunningVersion != cluster.Spec.Version
 
 	if useLocks && upgrading {
-		processGroupIDs := make([]string, 0, len(cluster.Status.ProcessGroups))
+		processGroupIDs := make([]fdbv1beta2.ProcessGroupID, 0, len(cluster.Status.ProcessGroups))
 		for _, processGroup := range cluster.Status.ProcessGroups {
 			processGroupIDs = append(processGroupIDs, processGroup.ProcessGroupID)
 		}
@@ -175,10 +175,10 @@ func (bounceProcesses) reconcile(ctx context.Context, r *FoundationDBClusterReco
 
 // getProcessesReadyForRestart returns a slice of process addresses that can be restarted. If addresses are missing or not all processes
 // have the latest configuration this method will return a requeue struct with more details.
-func getProcessesReadyForRestart(logger logr.Logger, cluster *fdbv1beta2.FoundationDBCluster, addressMap map[string][]fdbv1beta2.ProcessAddress, upgradedProcesses int) ([]fdbv1beta2.ProcessAddress, *requeue) {
+func getProcessesReadyForRestart(logger logr.Logger, cluster *fdbv1beta2.FoundationDBCluster, addressMap map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.ProcessAddress, upgradedProcesses int) ([]fdbv1beta2.ProcessAddress, *requeue) {
 	addresses := make([]fdbv1beta2.ProcessAddress, 0, len(cluster.Status.ProcessGroups))
 	allSynced := true
-	var missingAddress []string
+	var missingAddress []fdbv1beta2.ProcessGroupID
 
 	filterConditions := restarts.GetFilterConditions(cluster)
 	var missingProcesses int
@@ -255,9 +255,13 @@ func getAddressesForUpgrade(logger logr.Logger, r *FoundationDBClusterReconciler
 		return nil, &requeue{curError: err}
 	}
 
-	if !internal.HasDesiredFaultToleranceFromStatus(logger, databaseStatus, cluster) {
-		r.Recorder.Event(cluster, corev1.EventTypeNormal, "UpgradeRequeued", "Database is unavailable or doesn't have expected fault tolerance")
-		return nil, &requeue{message: "Deferring upgrade until database is available or expected fault tolerance is met"}
+	// We don't want to check for fault tolerance here to make sure the operator is able to restart processes if some
+	// processes where restarted before the operator issued the cluster wide restart. For version incompatible upgrades
+	// that would mean that the processes restarted earlier are not part of the cluster anymore leading to a fault tolerance
+	// drop.
+	if !databaseStatus.Client.DatabaseStatus.Available {
+		r.Recorder.Event(cluster, corev1.EventTypeNormal, "UpgradeRequeued", "Database is unavailable")
+		return nil, &requeue{message: "Deferring upgrade until database is available"}
 	}
 
 	notReadyProcesses := make([]string, 0)
@@ -267,7 +271,7 @@ func getAddressesForUpgrade(logger logr.Logger, r *FoundationDBClusterReconciler
 		if process.Version == version.String() {
 			continue
 		}
-		if pendingUpgrades[processID] {
+		if pendingUpgrades[fdbv1beta2.ProcessGroupID(processID)] {
 			addresses = append(addresses, process.Address)
 		} else {
 			notReadyProcesses = append(notReadyProcesses, processID)
@@ -295,7 +299,7 @@ func filterIgnoredProcessGroups(cluster *fdbv1beta2.FoundationDBCluster, address
 		return addresses, false
 	}
 
-	ignoredIDs := make(map[string]fdbv1beta2.None, len(cluster.Spec.Buggify.IgnoreDuringRestart))
+	ignoredIDs := make(map[fdbv1beta2.ProcessGroupID]fdbv1beta2.None, len(cluster.Spec.Buggify.IgnoreDuringRestart))
 	ignoredAddresses := make(map[string]fdbv1beta2.None, len(cluster.Spec.Buggify.IgnoreDuringRestart))
 
 	for _, id := range cluster.Spec.Buggify.IgnoreDuringRestart {
diff --git a/controllers/change_coordinators.go b/controllers/change_coordinators.go
index 663d02e3..3cea331c 100644
--- a/controllers/change_coordinators.go
+++ b/controllers/change_coordinators.go
@@ -131,7 +131,7 @@ func selectCandidates(cluster *fdbv1beta2.FoundationDBCluster, status *fdbv1beta
 			continue
 		}
 
-		if cluster.ProcessGroupIsBeingRemoved(process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey]) {
+		if cluster.ProcessGroupIsBeingRemoved(fdbv1beta2.ProcessGroupID(process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey])) {
 			continue
 		}
 
diff --git a/controllers/choose_removals.go b/controllers/choose_removals.go
index 29150c42..e1a155a0 100644
--- a/controllers/choose_removals.go
+++ b/controllers/choose_removals.go
@@ -39,7 +39,7 @@ func (c chooseRemovals) reconcile(ctx context.Context, r *FoundationDBClusterRec
 	logger := log.WithValues("namespace", cluster.Namespace, "cluster", cluster.Name, "reconciler", "chooseRemovals")
 	hasNewRemovals := false
 
-	var removals = make(map[string]bool)
+	var removals = make(map[fdbv1beta2.ProcessGroupID]bool)
 	for _, processGroup := range cluster.Status.ProcessGroups {
 		if processGroup.IsMarkedForRemoval() {
 			removals[processGroup.ProcessGroupID] = true
@@ -79,7 +79,7 @@ func (c chooseRemovals) reconcile(ctx context.Context, r *FoundationDBClusterRec
 			if processGroup.IsMarkedForRemoval() {
 				removedCount--
 			} else {
-				locality, present := localityMap[processGroup.ProcessGroupID]
+				locality, present := localityMap[string(processGroup.ProcessGroupID)]
 				if present {
 					processClassLocality = append(processClassLocality, locality)
 				}
@@ -113,7 +113,7 @@ func (c chooseRemovals) reconcile(ctx context.Context, r *FoundationDBClusterRec
 
 	if hasNewRemovals {
 		for _, processGroup := range cluster.Status.ProcessGroups {
-			if !remainingProcessMap[processGroup.ProcessGroupID] {
+			if !remainingProcessMap[string(processGroup.ProcessGroupID)] {
 				processGroup.MarkForRemoval()
 			}
 		}
diff --git a/controllers/delete_pods_for_buggification.go b/controllers/delete_pods_for_buggification.go
index 636eb977..4c47031e 100644
--- a/controllers/delete_pods_for_buggification.go
+++ b/controllers/delete_pods_for_buggification.go
@@ -46,7 +46,7 @@ func (d deletePodsForBuggification) reconcile(ctx context.Context, r *Foundation
 	podMap := internal.CreatePodMap(cluster, pods)
 	crashLoopContainerProcessGroups := cluster.GetCrashLoopContainerProcessGroups()
 
-	noSchedulePods := make(map[string]fdbv1beta2.None, len(cluster.Spec.Buggify.NoSchedule))
+	noSchedulePods := make(map[fdbv1beta2.ProcessGroupID]fdbv1beta2.None, len(cluster.Spec.Buggify.NoSchedule))
 	for _, processGroupID := range cluster.Spec.Buggify.NoSchedule {
 		noSchedulePods[processGroupID] = fdbv1beta2.None{}
 	}
diff --git a/controllers/exclude_processes.go b/controllers/exclude_processes.go
index fcb7df3a..b73fec98 100644
--- a/controllers/exclude_processes.go
+++ b/controllers/exclude_processes.go
@@ -128,12 +128,12 @@ func getProcessesToExclude(exclusions []fdbv1beta2.ProcessAddress, cluster *fdbv
 	return fdbProcessesToExclude, processClassesToExclude
 }
 
-func canExcludeNewProcesses(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2.ProcessClass) (bool, []string) {
+func canExcludeNewProcesses(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2.ProcessClass) (bool, []fdbv1beta2.ProcessGroupID) {
 	logger := log.WithValues("namespace", cluster.Namespace, "cluster", cluster.Name, "reconciler", "excludeProcesses")
 
 	// Block excludes on missing processes not marked for removal
-	missingProcesses := make([]string, 0)
-	validProcesses := make([]string, 0)
+	missingProcesses := make([]fdbv1beta2.ProcessGroupID, 0)
+	validProcesses := make([]fdbv1beta2.ProcessGroupID, 0)
 
 	for _, processGroupStatus := range cluster.Status.ProcessGroups {
 		if processGroupStatus.IsMarkedForRemoval() || processGroupStatus.ProcessClass != processClass {
diff --git a/controllers/remove_process_groups.go b/controllers/remove_process_groups.go
index ae2983e4..5c544d3c 100644
--- a/controllers/remove_process_groups.go
+++ b/controllers/remove_process_groups.go
@@ -125,7 +125,7 @@ func (u removeProcessGroups) reconcile(ctx context.Context, r *FoundationDBClust
 	return nil
 }
 
-func removeProcessGroup(ctx context.Context, r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, processGroupID string) error {
+func removeProcessGroup(ctx context.Context, r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, processGroupID fdbv1beta2.ProcessGroupID) error {
 	listOptions := internal.GetSinglePodListOptions(cluster, processGroupID)
 	pods, err := r.PodLifecycleManager.GetPods(ctx, r, cluster, listOptions...)
 	if err != nil {
@@ -174,7 +174,7 @@ func removeProcessGroup(ctx context.Context, r *FoundationDBClusterReconciler, c
 	return nil
 }
 
-func confirmRemoval(ctx context.Context, r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, processGroupID string) (bool, bool, error) {
+func confirmRemoval(ctx context.Context, r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, processGroupID fdbv1beta2.ProcessGroupID) (bool, bool, error) {
 	logger := log.WithValues("namespace", cluster.Namespace, "cluster", cluster.Name, "reconciler", "removeProcessGroups")
 	canBeIncluded := true
 	listOptions := internal.GetSinglePodListOptions(cluster, processGroupID)
@@ -233,7 +233,7 @@ func confirmRemoval(ctx context.Context, r *FoundationDBClusterReconciler, clust
 	return true, canBeIncluded, nil
 }
 
-func includeProcessGroup(ctx context.Context, r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, removedProcessGroups map[string]bool) error {
+func includeProcessGroup(ctx context.Context, r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, removedProcessGroups map[fdbv1beta2.ProcessGroupID]bool) error {
 	adminClient, err := r.getDatabaseClientProvider().GetAdminClient(cluster, r)
 	if err != nil {
 		return err
@@ -258,7 +258,7 @@ func includeProcessGroup(ctx context.Context, r *FoundationDBClusterReconciler,
 	return nil
 }
 
-func getProcessesToInclude(cluster *fdbv1beta2.FoundationDBCluster, removedProcessGroups map[string]bool) []fdbv1beta2.ProcessAddress {
+func getProcessesToInclude(cluster *fdbv1beta2.FoundationDBCluster, removedProcessGroups map[fdbv1beta2.ProcessGroupID]bool) []fdbv1beta2.ProcessAddress {
 	fdbProcessesToInclude := make([]fdbv1beta2.ProcessAddress, 0)
 
 	if len(removedProcessGroups) == 0 {
@@ -309,7 +309,7 @@ func (r *FoundationDBClusterReconciler) getProcessGroupsToRemove(cluster *fdbv1b
 			}
 		}
 
-		if _, ok := cordSet[processGroup.ProcessGroupID]; ok {
+		if _, ok := cordSet[string(processGroup.ProcessGroupID)]; ok {
 			logger.Info("Block removal of Coordinator", "processGroupID", processGroup.ProcessGroupID)
 			allExcluded = false
 			continue
@@ -338,7 +338,7 @@ func (r *FoundationDBClusterReconciler) getProcessGroupsToRemove(cluster *fdbv1b
 	return allExcluded, newExclusions, processGroupsToRemove
 }
 
-func (r *FoundationDBClusterReconciler) removeProcessGroups(ctx context.Context, cluster *fdbv1beta2.FoundationDBCluster, processGroupsToRemove []string, terminatingProcessGroups []string) map[string]bool {
+func (r *FoundationDBClusterReconciler) removeProcessGroups(ctx context.Context, cluster *fdbv1beta2.FoundationDBCluster, processGroupsToRemove []fdbv1beta2.ProcessGroupID, terminatingProcessGroups []fdbv1beta2.ProcessGroupID) map[fdbv1beta2.ProcessGroupID]bool {
 	logger := log.WithValues("namespace", cluster.Namespace, "cluster", cluster.Name, "reconciler", "removeProcessGroups")
 	r.Recorder.Event(cluster, corev1.EventTypeNormal, "RemovingProcesses", fmt.Sprintf("Removing pods: %v", processGroupsToRemove))
 
@@ -352,7 +352,7 @@ func (r *FoundationDBClusterReconciler) removeProcessGroups(ctx context.Context,
 		}
 	}
 
-	removedProcessGroups := make(map[string]bool)
+	removedProcessGroups := make(map[fdbv1beta2.ProcessGroupID]bool)
 	// We have to check if the currently removed process groups are completely removed.
 	// In addition, we have to check if one of the terminating process groups has been cleaned up.
 	for _, id := range processGroups {
diff --git a/controllers/update_pods.go b/controllers/update_pods.go
index 749c5ca7..d7a4dcd6 100644
--- a/controllers/update_pods.go
+++ b/controllers/update_pods.go
@@ -160,7 +160,7 @@ func (updatePods) reconcile(ctx context.Context, r *FoundationDBClusterReconcile
 	return deletePodsForUpdates(ctx, r, cluster, adminClient, updates, logger)
 }
 
-func shouldRequeueDueToTerminatingPod(pod *corev1.Pod, cluster *fdbv1beta2.FoundationDBCluster, processGroupID string) bool {
+func shouldRequeueDueToTerminatingPod(pod *corev1.Pod, cluster *fdbv1beta2.FoundationDBCluster, processGroupID fdbv1beta2.ProcessGroupID) bool {
 	return pod.DeletionTimestamp != nil &&
 		pod.DeletionTimestamp.Add(time.Duration(cluster.GetIgnoreTerminatingPodsSeconds())*time.Second).After(time.Now()) &&
 		!cluster.ProcessGroupIsBeingRemoved(processGroupID)
diff --git a/controllers/update_status.go b/controllers/update_status.go
index 5e340d72..8a5defc1 100644
--- a/controllers/update_status.go
+++ b/controllers/update_status.go
@@ -59,7 +59,7 @@ func (updateStatus) reconcile(ctx context.Context, r *FoundationDBClusterReconci
 	status.ImageTypes = []fdbv1beta2.ImageType{fdbv1beta2.ImageType(internal.GetDesiredImageType(cluster))}
 
 	var databaseStatus *fdbv1beta2.FoundationDBStatus
-	processMap := make(map[string][]fdbv1beta2.FoundationDBStatusProcessInfo)
+	processMap := make(map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.FoundationDBStatusProcessInfo)
 
 	if cluster.Status.ConnectionString == "" {
 		databaseStatus = &fdbv1beta2.FoundationDBStatus{
@@ -105,7 +105,7 @@ func (updateStatus) reconcile(ctx context.Context, r *FoundationDBClusterReconci
 		if !ok {
 			processID = process.Locality["instance_id"]
 		}
-		processMap[processID] = append(processMap[processID], process)
+		processMap[fdbv1beta2.ProcessGroupID(processID)] = append(processMap[fdbv1beta2.ProcessGroupID(processID)], process)
 		versionMap[process.Version]++
 	}
 
@@ -345,12 +345,12 @@ func tryConnectionOptions(logger logr.Logger, cluster *fdbv1beta2.FoundationDBCl
 }
 
 // checkAndSetProcessStatus checks the status of the Process and if missing or incorrect add it to the related status field
-func checkAndSetProcessStatus(r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod, processMap map[string][]fdbv1beta2.FoundationDBStatusProcessInfo, processNumber int, processCount int, processGroupStatus *fdbv1beta2.ProcessGroupStatus) error {
+func checkAndSetProcessStatus(r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod, processMap map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.FoundationDBStatusProcessInfo, processNumber int, processCount int, processGroupStatus *fdbv1beta2.ProcessGroupStatus) error {
 	logger := log.WithValues("namespace", cluster.Namespace, "cluster", cluster.Name, "reconciler", "updateStatus")
 	processID := processGroupStatus.ProcessGroupID
 
 	if processCount > 1 {
-		processID = fmt.Sprintf("%s-%d", processID, processNumber)
+		processID = fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-%d", processID, processNumber))
 	}
 
 	processStatus := processMap[processID]
@@ -395,10 +395,10 @@ func checkAndSetProcessStatus(r *FoundationDBClusterReconciler, cluster *fdbv1be
 	return nil
 }
 
-func validateProcessGroups(ctx context.Context, r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, status *fdbv1beta2.FoundationDBClusterStatus, processMap map[string][]fdbv1beta2.FoundationDBStatusProcessInfo, configMap *corev1.ConfigMap, pods []*corev1.Pod, pvcs *corev1.PersistentVolumeClaimList) ([]*fdbv1beta2.ProcessGroupStatus, error) {
+func validateProcessGroups(ctx context.Context, r *FoundationDBClusterReconciler, cluster *fdbv1beta2.FoundationDBCluster, status *fdbv1beta2.FoundationDBClusterStatus, processMap map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.FoundationDBStatusProcessInfo, configMap *corev1.ConfigMap, pods []*corev1.Pod, pvcs *corev1.PersistentVolumeClaimList) ([]*fdbv1beta2.ProcessGroupStatus, error) {
 	var err error
 	processGroups := status.ProcessGroups
-	processGroupsWithoutExclusion := make(map[string]fdbv1beta2.None, len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion))
+	processGroupsWithoutExclusion := make(map[fdbv1beta2.ProcessGroupID]fdbv1beta2.None, len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion))
 
 	for _, processGroupID := range cluster.Spec.ProcessGroupsToRemoveWithoutExclusion {
 		processGroupsWithoutExclusion[processGroupID] = fdbv1beta2.None{}
@@ -660,7 +660,7 @@ func refreshProcessGroupStatus(ctx context.Context, r *FoundationDBClusterReconc
 	}
 
 	for _, pod := range pods {
-		processGroupID := pod.Labels[cluster.GetProcessGroupIDLabel()]
+		processGroupID := fdbv1beta2.ProcessGroupID(pod.Labels[cluster.GetProcessGroupIDLabel()])
 		if fdbv1beta2.ContainsProcessGroupID(status.ProcessGroups, processGroupID) {
 			continue
 		}
@@ -675,7 +675,7 @@ func refreshProcessGroupStatus(ctx context.Context, r *FoundationDBClusterReconc
 	}
 
 	for _, pvc := range pvcs.Items {
-		processGroupID := pvc.Labels[cluster.GetProcessGroupIDLabel()]
+		processGroupID := fdbv1beta2.ProcessGroupID(pvc.Labels[cluster.GetProcessGroupIDLabel()])
 		if fdbv1beta2.ContainsProcessGroupID(status.ProcessGroups, processGroupID) {
 			continue
 		}
@@ -690,7 +690,7 @@ func refreshProcessGroupStatus(ctx context.Context, r *FoundationDBClusterReconc
 	}
 
 	for _, service := range services.Items {
-		processGroupID := service.Labels[cluster.GetProcessGroupIDLabel()]
+		processGroupID := fdbv1beta2.ProcessGroupID(service.Labels[cluster.GetProcessGroupIDLabel()])
 		if processGroupID == "" || fdbv1beta2.ContainsProcessGroupID(status.ProcessGroups, processGroupID) {
 			continue
 		}
diff --git a/docs/cluster_spec.md b/docs/cluster_spec.md
index a3275dcb..67cab5a7 100644
--- a/docs/cluster_spec.md
+++ b/docs/cluster_spec.md
@@ -57,11 +57,11 @@ BuggifyConfig provides options for injecting faults into a cluster for testing.
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| noSchedule | NoSchedule defines a list of process group IDs that should fail to schedule. | []string | false |
-| crashLoop | CrashLoops defines a list of process group IDs that should be put into a crash looping state. **Deprecated: use CrashLoopContainers instead.** | []string | false |
+| noSchedule | NoSchedule defines a list of process group IDs that should fail to schedule. | [][ProcessGroupID](#processgroupid) | false |
+| crashLoop | CrashLoops defines a list of process group IDs that should be put into a crash looping state. **Deprecated: use CrashLoopContainers instead.** | [][ProcessGroupID](#processgroupid) | false |
 | crashLoopContainers | CrashLoopContainers defines a list of process group IDs and containers that should be put into a crash looping state. | [][CrashLoopContainerObject](#crashloopcontainerobject) | false |
 | emptyMonitorConf | EmptyMonitorConf instructs the operator to update all of the fdbmonitor.conf files to have zero fdbserver processes configured. | bool | false |
-| ignoreDuringRestart | IgnoreDuringRestart instructs the operator to ignore the provided process groups IDs during the restart command. This can be useful to simulate cases where the kill command is not restarting all processes. IgnoreDuringRestart does not support the wildcard option to ignore all of this specific cluster processes. | []string | false |
+| ignoreDuringRestart | IgnoreDuringRestart instructs the operator to ignore the provided process groups IDs during the restart command. This can be useful to simulate cases where the kill command is not restarting all processes. IgnoreDuringRestart does not support the wildcard option to ignore all of this specific cluster processes. | [][ProcessGroupID](#processgroupid) | false |
 
 [Back to TOC](#table-of-contents)
 
@@ -145,7 +145,7 @@ CrashLoopContainerObject specifies crash-loop target for specific container.
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
 | containerName | Name of the target container. | string | false |
-| targets | Target processes to kill inside the container. | []string | false |
+| targets | Target processes to kill inside the container. | [][ProcessGroupID](#processgroupid) | false |
 
 [Back to TOC](#table-of-contents)
 
@@ -224,8 +224,8 @@ FoundationDBClusterSpec defines the desired state of a cluster.
 | seedConnectionString | SeedConnectionString provides a connection string for the initial reconciliation.  After the initial reconciliation, this will not be used. | string | false |
 | partialConnectionString | PartialConnectionString provides a way to specify part of the connection string (e.g. the database name and coordinator generation) without specifying the entire string. This does not allow for setting the coordinator IPs. If `SeedConnectionString` is set, `PartialConnectionString` will have no effect. They cannot be used together. | [ConnectionString](#connectionstring) | false |
 | faultDomain | FaultDomain defines the rules for what fault domain to replicate across. | [FoundationDBClusterFaultDomain](#foundationdbclusterfaultdomain) | false |
-| processGroupsToRemove | ProcessGroupsToRemove defines the process groups that we should remove from the cluster. This list contains the process group IDs. | []string | false |
-| processGroupsToRemoveWithoutExclusion | ProcessGroupsToRemoveWithoutExclusion defines the process groups that we should remove from the cluster without excluding them. This list contains the process group IDs.  This should be used for cases where a pod does not have an IP address and you want to remove it and destroy its volume without confirming the data is fully replicated. | []string | false |
+| processGroupsToRemove | ProcessGroupsToRemove defines the process groups that we should remove from the cluster. This list contains the process group IDs. | [][ProcessGroupID](#processgroupid) | false |
+| processGroupsToRemoveWithoutExclusion | ProcessGroupsToRemoveWithoutExclusion defines the process groups that we should remove from the cluster without excluding them. This list contains the process group IDs.  This should be used for cases where a pod does not have an IP address and you want to remove it and destroy its volume without confirming the data is fully replicated. | [][ProcessGroupID](#processgroupid) | false |
 | configMap | ConfigMap allows customizing the config map the operator creates. | *[corev1.ConfigMap](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#configmap-v1-core) | false |
 | mainContainer | MainContainer defines customization for the foundationdb container. | [ContainerOverrides](#containeroverrides) | false |
 | sidecarContainer | SidecarContainer defines customization for the foundationdb-kubernetes-sidecar container. | [ContainerOverrides](#containeroverrides) | false |
@@ -384,17 +384,23 @@ ProcessGroupConditionType represents a concrete ProcessGroupCondition.
 
 [Back to TOC](#table-of-contents)
 
+## ProcessGroupID
+
+ProcessGroupID represents the ID of the process group
+
+[Back to TOC](#table-of-contents)
+
 ## ProcessGroupStatus
 
 ProcessGroupStatus represents the status of a ProcessGroup.
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| processGroupID | ProcessGroupID represents the ID of the process group | string | false |
+| processGroupID | ProcessGroupID represents the ID of the process group | [ProcessGroupID](#processgroupid) | false |
 | processClass | ProcessClass represents the class the process group has. | [ProcessClass](#processclass) | false |
 | addresses | Addresses represents the list of addresses the process group has been known to have. | []string | false |
 | removalTimestamp | RemoveTimestamp if not empty defines when the process group was marked for removal. | *metav1.Time | false |
-| exclusionTimestamp | ExcludedTimestamp defines when the process group has been fully excluded. This is only used within the reconciliation process, and should not be considered authoritative. | *metav1.Time | false |
+| exclusionTimestamp | ExclusionTimestamp defines when the process group has been fully excluded. This is only used within the reconciliation process, and should not be considered authoritative. | *metav1.Time | false |
 | exclusionSkipped | ExclusionSkipped determines if exclusion has been skipped for a process, which will allow the process group to be removed without exclusion. | bool | false |
 | processGroupConditions | ProcessGroupConditions represents a list of degraded conditions that the process group is in. | []*[ProcessGroupCondition](#processgroupcondition) | false |
 
diff --git a/docs/manual/imgs/upgrade_mechanism.svg b/docs/manual/imgs/upgrade_mechanism.svg
new file mode 100644
index 00000000..af6f34c5
--- /dev/null
+++ b/docs/manual/imgs/upgrade_mechanism.svg
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="-235 120 1668 523" width="1668" height="523">
+  <defs>
+    <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" stroke-linejoin="miter" stroke-miterlimit="10" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black">
+      <g>
+        <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/>
+      </g>
+    </marker>
+  </defs>
+  <g id="Canvas_1" stroke="none" fill="none" stroke-dasharray="none" stroke-opacity="1" fill-opacity="1">
+    <title>Canvas 1</title>
+    <rect fill="white" x="-235" y="120" width="1668" height="523"/>
+    <g id="Canvas_1_Layer_1">
+      <title>Layer 1</title>
+      <g id="Graphic_2">
+        <rect x="-234.5" y="120.5" width="428" height="522" fill="white"/>
+        <rect x="-234.5" y="120.5" width="428" height="522" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(-229.5 619.052)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="173.296" y="15">Storage-1</tspan>
+        </text>
+      </g>
+      <g id="Graphic_3">
+        <rect x="-212" y="150.5" width="383.5" height="131.5" fill="#c0ffff"/>
+        <rect x="-212" y="150.5" width="383.5" height="131.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(-207 207.026)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="34.702" y="15">foundationdb-kubernetes-sidecar:6.3.25-1</tspan>
+        </text>
+      </g>
+      <g id="Graphic_4">
+        <rect x="-212" y="463.5" width="383.5" height="131.5" fill="#c0ffff"/>
+        <rect x="-212" y="463.5" width="383.5" height="131.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(-207 520.026)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="114.566" y="15">foundationdb:6.3.25</tspan>
+        </text>
+      </g>
+      <g id="Graphic_5">
+        <rect x="-212" y="317.25" width="383.5" height="111" fill="#ccc"/>
+        <rect x="-212" y="317.25" width="383.5" height="111" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(-207 345.078)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="15">/bin/6.3.25/fdbserver</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="33.448">/fdbmonitor.conf (pointing to /usr/..)</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="51.895996">/fdb.cluster</tspan>
+        </text>
+      </g>
+      <g id="Line_6">
+        <line x1="-20.25" y1="463.5" x2="-20.25" y2="438.15" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
+      </g>
+      <g id="Line_7">
+        <line x1="-20.25" y1="282" x2="-20.25" y2="307.35" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
+      </g>
+      <g id="Graphic_13">
+        <rect x="385" y="120.5" width="428" height="522" fill="white"/>
+        <rect x="385" y="120.5" width="428" height="522" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(390 619.052)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="173.296" y="15">Storage-1</tspan>
+        </text>
+      </g>
+      <g id="Graphic_12">
+        <rect x="407.5" y="150.5" width="383.5" height="131.5" fill="#c0ffc0"/>
+        <rect x="407.5" y="150.5" width="383.5" height="131.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(412.5 207.026)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="34.702" y="15">foundationdb-kubernetes-sidecar:7.1.27-1</tspan>
+        </text>
+      </g>
+      <g id="Graphic_11">
+        <rect x="407.5" y="463.5" width="383.5" height="131.5" fill="#c0ffff"/>
+        <rect x="407.5" y="463.5" width="383.5" height="131.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(412.5 520.026)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="114.566" y="15">foundationdb:6.3.25</tspan>
+        </text>
+      </g>
+      <g id="Graphic_10">
+        <rect x="407.5" y="317.25" width="383.5" height="111" fill="#ccc"/>
+        <rect x="407.5" y="317.25" width="383.5" height="111" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(412.5 345.078)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="15">/bin/7.1.27/fdbserver</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="33.448">/fdbmonitor.conf (pointing to /var/..)</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="51.895996">/fdb.cluster</tspan>
+        </text>
+      </g>
+      <g id="Line_9">
+        <line x1="599.25" y1="463.5" x2="599.25" y2="438.15" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
+      </g>
+      <g id="Line_8">
+        <line x1="599.25" y1="282" x2="599.25" y2="307.35" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
+      </g>
+      <g id="Line_14">
+        <line x1="193.5" y1="381.5" x2="375.1" y2="381.5" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
+      </g>
+      <g id="Graphic_16">
+        <text transform="translate(214.386 349)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="15">Update Sidecar image</tspan>
+        </text>
+      </g>
+      <g id="Graphic_23">
+        <rect x="1004.5" y="120.5" width="428" height="522" fill="white"/>
+        <rect x="1004.5" y="120.5" width="428" height="522" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(1009.5 619.052)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="173.296" y="15">Storage-1</tspan>
+        </text>
+      </g>
+      <g id="Graphic_22">
+        <rect x="1027" y="150.5" width="383.5" height="131.5" fill="#c0ffc0"/>
+        <rect x="1027" y="150.5" width="383.5" height="131.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(1032 207.026)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="34.702" y="15">foundationdb-kubernetes-sidecar:7.1.27-1</tspan>
+        </text>
+      </g>
+      <g id="Graphic_21">
+        <rect x="1027" y="463.5" width="383.5" height="131.5" fill="#c0ffc0"/>
+        <rect x="1027" y="463.5" width="383.5" height="131.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(1032 520.026)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="114.566" y="15">foundationdb:7.1.27</tspan>
+        </text>
+      </g>
+      <g id="Graphic_20">
+        <rect x="1027" y="317.25" width="383.5" height="111" fill="#ccc"/>
+        <rect x="1027" y="317.25" width="383.5" height="111" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(1032 345.078)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="15">/bin/7.1.27/fdbserver</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="33.448">/fdbmonitor.conf (pointing to /usr/…)</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="51.895996">/fdb.cluster</tspan>
+        </text>
+      </g>
+      <g id="Line_19">
+        <line x1="1218.75" y1="463.5" x2="1218.75" y2="438.15" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
+      </g>
+      <g id="Line_18">
+        <line x1="1218.75" y1="282" x2="1218.75" y2="307.35" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
+      </g>
+      <g id="Line_24">
+        <line x1="813" y1="381.5" x2="994.6" y2="381.5" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
+      </g>
+      <g id="Graphic_25">
+        <text transform="translate(860.006 349)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="5897505e-19" y="15">Recreate Pod</tspan>
+        </text>
+      </g>
+      <g id="Graphic_30">
+        <text transform="translate(-154.5 404.802)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="15">Shared volume /var/dynamic-config</tspan>
+        </text>
+      </g>
+      <g id="Graphic_31">
+        <text transform="translate(460.272 404.802)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="15">Shared volume /var/dynamic-config</tspan>
+        </text>
+      </g>
+      <g id="Graphic_32">
+        <text transform="translate(1074.272 404.802)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" fill="black" x="0" y="15">Shared volume /var/dynamic-config</tspan>
+        </text>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/docs/manual/index.md b/docs/manual/index.md
index 5fa3c9a9..d2b2c5fd 100644
--- a/docs/manual/index.md
+++ b/docs/manual/index.md
@@ -19,5 +19,6 @@ For more information on that area, see the [Kubernetes documentation](https://ku
 1. [Running with TLS](tls.md)
 1. [Backup](backup.md)
 1. [Technical Design](technical_design.md)
+1. [Upgrades](upgrades.md)
 1. [Debugging](debugging.md)
 1. [More References](more.md)
diff --git a/docs/manual/technical_design.md b/docs/manual/technical_design.md
index ea46cc94..18633109 100644
--- a/docs/manual/technical_design.md
+++ b/docs/manual/technical_design.md
@@ -397,4 +397,4 @@ All of the flows above go through the `foundationdb` container. The `foundationd
 
 ## Next
 
-You can continue on to the [next section](debugging.md) or go back to the [table of contents](index.md).
+You can continue on to the [next section](upgrades.md) or go back to the [table of contents](index.md).
diff --git a/docs/manual/upgrades.md b/docs/manual/upgrades.md
new file mode 100644
index 00000000..cb35e979
--- /dev/null
+++ b/docs/manual/upgrades.md
@@ -0,0 +1,173 @@
+# FoundationDB Upgrades
+
+## Overview
+
+The FDB operator supports FDB clusters upgrades by adjusting the `version` setting in the `FoundationDBCluster` spec to the new desired version.
+It also offers limited support for downgrades, the limitation is that the operator only supports downgrades to an older patch version of the running FDB version.
+Upgrades of FoundationDB running on Kubernetes are challenging and the operator provides a way to handle a lot of the complexity on itself.
+The document will describe the upgrade process in details and some cases the operator cannot handle currently.
+
+FoundationDB upgrades for major and minor versions are incompatible and require that all processes are updated in a short time period.
+
+### Upgrade Process Details
+
+This document describes the upgrade process based on the operator version `v1.14.0` for older versions there might be a slight difference in how the operator is handling the upgrades.
+In addition to that we are currently working on reducing the complexity for patch upgrades to reduce the upgrade steps to the [Recreation of old Pods](#recreation-of-pods-phase) phase.
+
+The graphic below represents the different stages in a Pod during the upgrade:
+
+<img src="./imgs/upgrade_mechanism.svg">
+
+The first state on the left is the initial state before the operator is doing any work.
+The second state in the middle is the state of the Pod during the [Staging Phase](#staging-phase) and the [Restart Phase](#restart-phase).
+The third and last state is the sta te after the [Recreation of Pods Phase](#recreation-of-pods-phase) where the according Pod was recreated with the new desired image version.
+
+
+#### Pre Upgrade Check Phase
+
+Before the upgrade is initiated the operator will perform a compatibility check for all clients of this cluster.
+This check will ensure that a cluster is only upgraded if all clients support the new version, for patch upgrades this check is skipped.
+More information about this reconciler can be found in the [technical design](technical_design.md#checkclientcompatibility).
+Clients not supporting the new version will be reported in the logs of the operator with the message `Deferring reconciliation due to unsupported clients` and in addition the operator will emit a Kubernetes event.
+This prevents upgrading a database until all clients have been updated with a compatible client library.
+
+#### Staging Phase
+
+This phase ensures that all Pods are in a state that the operator can restart all `fdbserver` processes and they will be restarting with the new FDB version.
+The first step is the `UpdateSidecarVersions`, this subreconciler updates the image of the `foundationdb-kubernetes-sidecar` container to match the desired FDB version.
+Updating the image of a single container doesn't require the recreation of the Pod and keeps the main container that runs the `fdbserver` process running.
+The sidecar container will copy the new binary inside the main container over a shared volume.
+In the main container the new binaries will be present in `/var/dynamic-conf/bin/$fdb_version`, this version will be used once the `fdbserver` process is restarted.
+The `UpdatePodConfig` subreconciler will ensure that all Pods have the new configuration present and that the new binary is present in the shared volume.
+This configuration ensures that the `fdbmonitor` will restart processes with the new binary located at `/var/dynamic-conf/bin/$fdb_version`.
+Once all Pods have the new configuration and binaries present the operator can move to the next phase.
+
+A potentional blocker for moving towards the next Phase can be an issue with a subset of the Pods e.g. if the sidecar is unreachable.
+If the sidecar is unreachable the operator is not able to confirm that the new configuration and binary is in place.
+All Process Groups affected by such an issue can be discovered with the `SidecarUnreachable` condition, until this issue is resolved the upgrade will be blocked.
+The best way forward for this is to replace the affected Pods with the following [kubectl fdb plugin](../../kubectl-fdb/Readme.md) command:
+
+```bash
+kubectl fdb remove process-group -c cluster pod-1
+```
+
+If the whole Pod is affected by the networking issue the operator will replace the Pod automatically if automatic replacements are enabled, which is the default.
+In order to replace the Pod the operator will wait for the Process Group to have a [failure condition](https://github.com/FoundationDB/fdb-kubernetes-operator/blob/v1.14.0/api/v1beta2/foundationdbcluster_types.go#L65) for at least the defined [FailureDetectionTimeSeconds](../cluster_spec.md#automaticreplacementoptions), by default those are 7200 seconds (2 hours).
+
+#### Restart Phase
+
+The `BounceProcesses` subreconciler will handle the restart of all `fdbserver` processes.
+For FoundationDB clusters that are spanned across multiple Kubernetes clusters the operator will follow a special process which is document in the [technical design](technical_design.md#bounceprocesses).
+The subreconciler will also ensure to wait until all `fdbserver` processes are ready to be restarted to prevent cases where only a subset of processes are restarted.
+In this case ready means that all Pods have the new `fdbmonitor` configuration present and that the new binary is present in the shared volume at `/var/dynamic-conf/bin/$fdb_version`.
+After the `kill` command the operator will initiate a new reconciliation loop to detect the new running version, this is handled in the `UpdateState` subreconciler and the version is detected based on the output of the [cluster status json](https://apple.github.io/foundationdb/mr-status.html).
+
+#### Recreation of Pods Phase
+
+Technically the upgrade is now done and the cluster is running on the new version but the operator has to do some cleanup.
+The cleanup includes the recreation of all Pods to ensure they use the new FoundationDB image for the desired version and the configuration will be updated to point again to the binary present in the container.
+Depending on the defined [Pod update strategy](../cluster_spec.md#podupdatestrategy) the operator will handle the recreation of the Pods differently.
+In the default setting `ReplaceTransactionSystem` the operator will recreate the storage Pods zone by zone and replaces Pods of the transaction system (log and stateless processes).
+The replacement of the transaction system has the benefit that the recoveries of the transaction system is reduced to potentially one recovery.
+During this phase the operator will also run the `RemoveIncompatibleProcesses` subreconciler, this reconciler will recreate all Pods that host a `fdbserver` process that is in the list of the `incompatible_connections` and is missing the the process list of the [cluster status json](https://apple.github.io/foundationdb/mr-status.html).
+This reconciler should ensure that the cluster is moving faster towards the desired configuration if some processes where not restarted properly.
+
+Once all Pods are updated to the new image the upgrade is done and the cluster status of the FoundationDB cluster resource in Kubernetes should show that the reconciliation is done.
+
+### Known issues
+
+There are a number of known issues that can occur during an upgrade of FoundationDB running on Kubernetes.
+Most of the issues are only a challenge for version incompatible upgrades and most issues are handled automatically by the operator.
+If an issue cannot be handled by the operator we will outline a way to manually resolve the issue.
+
+#### Process restarted during staging Phase
+
+During the staging phase it can happen that a `fdbserver` process gets restarted by an external event like the OOM killer or any issue that results in the `fdbserver` crashing.
+In that case the `fdbmonitor` will restart the `fdbserver` process with the new version and for version incompatible upgrades, that means this process is not able to connect to the cluster.
+The operator will mark this Process Group with the `MissingProcesses` condition, since the process will be missing in the [cluster status json](https://apple.github.io/foundationdb/mr-status.html).
+If the process has this condition longer than the [IgnoreMissingProcessesSeconds](../cluster_spec.md#foundationdbclusterautomationoptions) setting it will be ignored in the [Restart Phase](#restart-phase) to ensure the operator can move forward with the upgrade.
+This also means that for a short time the cluster might be running with less resources.
+
+Processes that are restarted too early can be identified with the `MissingProcesses` condition.
+Using `kubectl` to fetch the cluster status and searching for all Process Groups with this condition gives an overview of those processes restarted to early.
+
+The operator will handle those cases automatically and there is no need for manual intervention as long as not too many processes are restarted from the outside.
+In that case the best way forward is to restart all processes still running on the old version, this can either be done manually or by using the [kubectl fdb plugin](../../kubectl-fdb/Readme.md) with the following command:
+
+```bash
+kubectl fdb restart -c cluster --process-condition=IncorrectCommandLine
+```
+
+One special case here is when the majority of the coordinators are restarted and the rest of the processes are still running on the old version.
+In this case the operator is not able to handle the upgrade correctly since the returned cluster status has no information besides the coordinators that are reachable.
+During this time the FDB cluster will be unavailable.
+You have to manually restart all processes with the [kubectl fdb plugin](../../kubectl-fdb/Readme.md) with the command from above.
+
+#### Processes are not restarted during the restart Phase
+
+This issue is similar to the one above with the difference that affected processes are not restarted and they are still running on the old `fdbserver` version.
+For version incompatible upgrades that means that those processes are not reporting to the cluster.
+The operator will mark this Process Group with the `MissingProcesses` condition, since the process will be missing in the [cluster status json](https://apple.github.io/foundationdb/mr-status.html).
+If those processes are reported in the `incompatible_connections` field of the cluster status json the operator will recreate those Pods directly with the new image in the `RemoveIncompatibleProcesses` subreconciler.
+Otherwise the Pod will recreate those Pods in the [recreation of Pods phase](#recreation-of-pods-phase) with the new image.
+During that time the cluster might run with reduced capacity since not all processes are reporting to the cluster.
+
+The operator can handle those cases automatically.
+If you want to speed up this process you can restart the processes with the [kubectl fdb plugin](../../kubectl-fdb/Readme.md):
+
+```bash
+kubectl fdb restart -c cluster --process-condition=MissingProcesses
+```
+
+One special case here is when the majority of coordinators are not restarted and they are stuck on the old version.
+In that case the operator is not able to handle this case automatically and you have to restart the coordinator processes manually with the [kubectl fdb plugin](../../kubectl-fdb/Readme.md).
+Either by targeting directly the coordinator Pods (you can get the coordinator Pods by comparing the IPs of the connection string with the IP addresses of the Pods):
+
+```bash
+kubectl fdb -n default restart -c cluster pod-1 pod-2
+```
+
+or by restarting all processes of the cluster:
+
+```bash
+kubectl fdb restart -c cluster --all-processes
+```
+
+#### Detect correct version
+
+The operator tries per default to read the [cluster status json](https://apple.github.io/foundationdb/mr-status.html) with the [multi-version](https://apple.github.io/foundationdb/api-general.html#multi-version-client) from the system key-space.
+During an upgrade it can happen that the multi-version client tries to connect to the cluster with the wrong version especially if not all coordinators are upgraded.
+In this case the operator will fallback using `fdbcli` for the different versions (the one that is desired and the current running version).
+This requires that the majority of coordinators are upgraded to the new desired version.
+In addition to that we will retry the command if the `fdbcli` hits a timeout responding.
+The code in the [fdbclient package](https://github.com/FoundationDB/fdb-kubernetes-operator/blob/v1.14.0/fdbclient/admin_client.go#L337-L360) has some additional details on how we handle those cases.
+
+#### Processes running on a different version
+
+We discussed in [Process restarted during staging Phase](#process-restarted-during-staging-phase) and [Processes are not restarted during the restart Phase](#processes-are-not-restarted-during-the-restart-phase) cases where a process could be running in a different version than expected.
+For version incompatible upgrades the operator has no way to differentiate between processes not reporting to the cluster and processes running in the incompatible version, both will be missing from the processes list.
+The only difference is that the incompatible processes might be reported in the `incompatible_connections` field.
+
+### How we test upgrades in the operator
+
+For every PR against the operator repository we run a set of tests in addition to that we are running those tests on a regular basis with different versions to ensure we test major, minor and patch upgrades.
+
+Those tests include:
+
+- Upgrades without any failure injection
+- Upgrading a cluster with a partitioned Pod
+- Upgrading a cluster with a partitioned Pod which eventually gets replaced
+- Upgrading a cluster with a random Pod deleted during rolling bounce phase
+- Upgrading a cluster where one coordinator gets restarted during the staging phase
+- Upgrading a cluster with a crash looping sidecar process
+- Upgrading a cluster and one coordinator is not restarted
+- Upgrading a cluster and multiple processes are not restarted
+- Upgrading a cluster with link that drops some packets
+- Upgrading a cluster and no coordinator is restarted
+
+In addition we have some more tests that will run against a cluster in multi-region configuration.
+Those tests are extended if some new edge cases are discovered.
+
+## Next
+
+You can continue on to the [next section](debugging.md) or go back to the [table of contents](index.md).
diff --git a/fdbclient/lock_client.go b/fdbclient/lock_client.go
index f65a6378..790b5009 100644
--- a/fdbclient/lock_client.go
+++ b/fdbclient/lock_client.go
@@ -157,7 +157,7 @@ func (client *realLockClient) updateLock(transaction fdb.Transaction, start int6
 
 // AddPendingUpgrades registers information about which process groups are
 // pending an upgrade to a new version.
-func (client *realLockClient) AddPendingUpgrades(version fdbv1beta2.Version, processGroupIDs []string) error {
+func (client *realLockClient) AddPendingUpgrades(version fdbv1beta2.Version, processGroupIDs []fdbv1beta2.ProcessGroupID) error {
 	_, err := client.database.Transact(func(tr fdb.Transaction) (interface{}, error) {
 		err := tr.Options().SetAccessSystemKeys()
 		if err != nil {
@@ -174,7 +174,7 @@ func (client *realLockClient) AddPendingUpgrades(version fdbv1beta2.Version, pro
 
 // GetPendingUpgrades returns the stored information about which process
 // groups are pending an upgrade to a new version.
-func (client *realLockClient) GetPendingUpgrades(version fdbv1beta2.Version) (map[string]bool, error) {
+func (client *realLockClient) GetPendingUpgrades(version fdbv1beta2.Version) (map[fdbv1beta2.ProcessGroupID]bool, error) {
 	upgrades, err := client.database.Transact(func(tr fdb.Transaction) (interface{}, error) {
 		err := tr.Options().SetReadSystemKeys()
 		if err != nil {
@@ -187,19 +187,23 @@ func (client *realLockClient) GetPendingUpgrades(version fdbv1beta2.Version) (ma
 			return nil, err
 		}
 		results := tr.GetRange(keyRange, fdb.RangeOptions{}).GetSliceOrPanic()
-		upgrades := make(map[string]bool, len(results))
+		upgrades := make(map[fdbv1beta2.ProcessGroupID]bool, len(results))
 		for _, result := range results {
-			upgrades[string(result.Value)] = true
+			upgrades[fdbv1beta2.ProcessGroupID(result.Value)] = true
 		}
+
 		return upgrades, nil
 	})
+
 	if err != nil {
 		return nil, err
 	}
-	upgradeMap, isMap := upgrades.(map[string]bool)
+
+	upgradeMap, isMap := upgrades.(map[fdbv1beta2.ProcessGroupID]bool)
 	if !isMap {
 		return nil, fmt.Errorf("invalid return value from transaction in GetPendingUpgrades: %v", upgrades)
 	}
+
 	return upgradeMap, nil
 }
 
@@ -221,6 +225,7 @@ func (client *realLockClient) ClearPendingUpgrades() error {
 		tr.ClearRange(keyRange)
 		return nil, nil
 	})
+
 	return err
 }
 
diff --git a/internal/deprecations.go b/internal/deprecations.go
index 922ea5e5..1c856fc1 100644
--- a/internal/deprecations.go
+++ b/internal/deprecations.go
@@ -100,7 +100,7 @@ func NormalizeClusterSpec(cluster *fdbv1beta2.FoundationDBCluster, options Depre
 	if len(cluster.Spec.Buggify.CrashLoop) > 0 {
 		crashLoopContainers := cluster.GetCrashLoopContainerProcessGroups()
 		if _, ok := crashLoopContainers[fdbv1beta2.MainContainerName]; !ok {
-			crashLoopContainers[fdbv1beta2.MainContainerName] = make(map[string]fdbv1beta2.None)
+			crashLoopContainers[fdbv1beta2.MainContainerName] = make(map[fdbv1beta2.ProcessGroupID]fdbv1beta2.None)
 		}
 
 		for _, pid := range cluster.Spec.Buggify.CrashLoop {
@@ -111,7 +111,7 @@ func NormalizeClusterSpec(cluster *fdbv1beta2.FoundationDBCluster, options Depre
 		i := 0
 		for name, procs := range crashLoopContainers {
 			result[i].ContainerName = name
-			result[i].Targets = make([]string, len(procs))
+			result[i].Targets = make([]fdbv1beta2.ProcessGroupID, len(procs))
 
 			j := 0
 			for pid := range procs {
diff --git a/internal/foundationdb_status.go b/internal/foundationdb_status.go
index f77218cf..c485eeed 100644
--- a/internal/foundationdb_status.go
+++ b/internal/foundationdb_status.go
@@ -50,7 +50,7 @@ func GetCoordinatorsFromStatus(status *fdbv1beta2.FoundationDBStatus) map[string
 // GetMinimumUptimeAndAddressMap returns address map of the processes included the the foundationdb status. The minimum
 // uptime will be either secondsSinceLastRecovered if the recovery state is supported and enabled otherwise we will
 // take the minimum uptime of all processes.
-func GetMinimumUptimeAndAddressMap(cluster *fdbv1beta2.FoundationDBCluster, status *fdbv1beta2.FoundationDBStatus, recoveryStateEnabled bool) (float64, map[string][]fdbv1beta2.ProcessAddress, error) {
+func GetMinimumUptimeAndAddressMap(cluster *fdbv1beta2.FoundationDBCluster, status *fdbv1beta2.FoundationDBStatus, recoveryStateEnabled bool) (float64, map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.ProcessAddress, error) {
 	runningVersion, err := fdbv1beta2.ParseFdbVersion(cluster.GetRunningVersion())
 	if err != nil {
 		return 0, nil, err
@@ -58,7 +58,7 @@ func GetMinimumUptimeAndAddressMap(cluster *fdbv1beta2.FoundationDBCluster, stat
 
 	useRecoveryState := runningVersion.SupportsRecoveryState() && recoveryStateEnabled
 
-	addressMap := make(map[string][]fdbv1beta2.ProcessAddress, len(status.Cluster.Processes))
+	addressMap := make(map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.ProcessAddress, len(status.Cluster.Processes))
 
 	minimumUptime := math.Inf(1)
 	if useRecoveryState {
@@ -66,7 +66,7 @@ func GetMinimumUptimeAndAddressMap(cluster *fdbv1beta2.FoundationDBCluster, stat
 	}
 
 	for _, process := range status.Cluster.Processes {
-		addressMap[process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey]] = append(addressMap[process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey]], process.Address)
+		addressMap[fdbv1beta2.ProcessGroupID(process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey])] = append(addressMap[fdbv1beta2.ProcessGroupID(process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey])], process.Address)
 
 		if useRecoveryState || process.Excluded {
 			continue
diff --git a/internal/locality/locality.go b/internal/locality/locality.go
index ba56f0db..87134bbd 100644
--- a/internal/locality/locality.go
+++ b/internal/locality/locality.go
@@ -264,7 +264,7 @@ func CheckCoordinatorValidity(logger logr.Logger, cluster *fdbv1beta2.Foundation
 
 	coordinatorZones := make(map[string]int, len(coordinatorStatus))
 	coordinatorDCs := make(map[string]int, len(coordinatorStatus))
-	processGroups := make(map[string]*fdbv1beta2.ProcessGroupStatus)
+	processGroups := make(map[fdbv1beta2.ProcessGroupID]*fdbv1beta2.ProcessGroupStatus)
 	for _, processGroup := range cluster.Status.ProcessGroups {
 		processGroups[processGroup.ProcessGroupID] = processGroup
 	}
@@ -281,7 +281,7 @@ func CheckCoordinatorValidity(logger logr.Logger, cluster *fdbv1beta2.Foundation
 			continue
 		}
 
-		processGroupStatus := processGroups[process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey]]
+		processGroupStatus := processGroups[fdbv1beta2.ProcessGroupID(process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey])]
 		pendingRemoval := processGroupStatus != nil && processGroupStatus.IsMarkedForRemoval()
 		if processGroupStatus != nil && cluster.SkipProcessGroup(processGroupStatus) {
 			pLogger.Info("Skipping process group with pending Pod",
diff --git a/internal/pod_client.go b/internal/pod_client.go
index 687aa808..090f2452 100644
--- a/internal/pod_client.go
+++ b/internal/pod_client.go
@@ -482,7 +482,7 @@ func GetSubstitutionsFromClusterAndPod(logger logr.Logger, cluster *fdbv1beta2.F
 		}
 	}
 
-	substitutions["FDB_INSTANCE_ID"] = GetProcessGroupIDFromMeta(cluster, pod.ObjectMeta)
+	substitutions["FDB_INSTANCE_ID"] = string(GetProcessGroupIDFromMeta(cluster, pod.ObjectMeta))
 
 	if cluster.IsBeingUpgraded() {
 		substitutions["BINARY_DIR"] = fmt.Sprintf("/var/dynamic-conf/bin/%s", cluster.Spec.Version)
diff --git a/internal/pod_helper.go b/internal/pod_helper.go
index d9d37644..bc2068bf 100644
--- a/internal/pod_helper.go
+++ b/internal/pod_helper.go
@@ -97,8 +97,8 @@ func GetPublicIPsForPod(pod *corev1.Pod, log logr.Logger) []string {
 }
 
 // GetProcessGroupIDFromMeta fetches the process group ID from an object's metadata.
-func GetProcessGroupIDFromMeta(cluster *fdbv1beta2.FoundationDBCluster, metadata metav1.ObjectMeta) string {
-	return metadata.Labels[cluster.GetProcessGroupIDLabel()]
+func GetProcessGroupIDFromMeta(cluster *fdbv1beta2.FoundationDBCluster, metadata metav1.ObjectMeta) fdbv1beta2.ProcessGroupID {
+	return fdbv1beta2.ProcessGroupID(metadata.Labels[cluster.GetProcessGroupIDLabel()])
 }
 
 // GetPodSpecHash builds the hash of the expected spec for a pod.
@@ -181,8 +181,8 @@ func BuildOwnerReference(ownerType metav1.TypeMeta, ownerMetadata metav1.ObjectM
 }
 
 // GetSinglePodListOptions returns the listOptions to list a single Pod
-func GetSinglePodListOptions(cluster *fdbv1beta2.FoundationDBCluster, processGroupID string) []client.ListOption {
-	return []client.ListOption{client.InNamespace(cluster.ObjectMeta.Namespace), client.MatchingLabels(GetPodMatchLabels(cluster, "", processGroupID))}
+func GetSinglePodListOptions(cluster *fdbv1beta2.FoundationDBCluster, processGroupID fdbv1beta2.ProcessGroupID) []client.ListOption {
+	return []client.ListOption{client.InNamespace(cluster.ObjectMeta.Namespace), client.MatchingLabels(GetPodMatchLabels(cluster, "", string(processGroupID)))}
 }
 
 // GetPodListOptions returns the listOptions to list Pods
@@ -191,7 +191,7 @@ func GetPodListOptions(cluster *fdbv1beta2.FoundationDBCluster, processClass fdb
 }
 
 // GetPvcMetadata returns the metadata for a PVC
-func GetPvcMetadata(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2.ProcessClass, id string) metav1.ObjectMeta {
+func GetPvcMetadata(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2.ProcessClass, id fdbv1beta2.ProcessGroupID) metav1.ObjectMeta {
 	var customMetadata *metav1.ObjectMeta
 
 	processSettings := cluster.GetProcessSettings(processClass)
@@ -227,8 +227,8 @@ func GetSidecarImage(cluster *fdbv1beta2.FoundationDBCluster, pClass fdbv1beta2.
 }
 
 // CreatePodMap creates a map with the process group ID as a key and the according Pod as a value
-func CreatePodMap(cluster *fdbv1beta2.FoundationDBCluster, pods []*corev1.Pod) map[string]*corev1.Pod {
-	podProcessGroupMap := make(map[string]*corev1.Pod, len(pods))
+func CreatePodMap(cluster *fdbv1beta2.FoundationDBCluster, pods []*corev1.Pod) map[fdbv1beta2.ProcessGroupID]*corev1.Pod {
+	podProcessGroupMap := make(map[fdbv1beta2.ProcessGroupID]*corev1.Pod, len(pods))
 	for _, pod := range pods {
 		processGroupID := GetProcessGroupIDFromMeta(cluster, pod.ObjectMeta)
 		if processGroupID == "" {
@@ -241,8 +241,8 @@ func CreatePodMap(cluster *fdbv1beta2.FoundationDBCluster, pods []*corev1.Pod) m
 }
 
 // ParseProcessGroupID extracts the components of an process group ID.
-func ParseProcessGroupID(id string) (fdbv1beta2.ProcessClass, int, error) {
-	result := processGroupIDRegex.FindStringSubmatch(id)
+func ParseProcessGroupID(id fdbv1beta2.ProcessGroupID) (fdbv1beta2.ProcessClass, int, error) {
+	result := processGroupIDRegex.FindStringSubmatch(string(id))
 	if result == nil {
 		return "", 0, fmt.Errorf("could not parse process group ID %s", id)
 	}
diff --git a/internal/pod_models.go b/internal/pod_models.go
index 6ff69217..729a164d 100644
--- a/internal/pod_models.go
+++ b/internal/pod_models.go
@@ -38,25 +38,25 @@ import (
 var processClassSanitizationPattern = regexp.MustCompile("[^a-z0-9-]")
 
 // GetProcessGroupIDFromPodName returns the process group ID for a given Pod name.
-func GetProcessGroupIDFromPodName(cluster *fdbv1beta2.FoundationDBCluster, podName string) string {
+func GetProcessGroupIDFromPodName(cluster *fdbv1beta2.FoundationDBCluster, podName string) fdbv1beta2.ProcessGroupID {
 	tmpName := strings.ReplaceAll(podName, cluster.Name, "")[1:]
 
 	if cluster.Spec.ProcessGroupIDPrefix != "" {
-		return fmt.Sprintf("%s-%s", cluster.Spec.ProcessGroupIDPrefix, tmpName)
+		return fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-%s", cluster.Spec.ProcessGroupIDPrefix, tmpName))
 	}
 
-	return tmpName
+	return fdbv1beta2.ProcessGroupID(tmpName)
 }
 
 // GetProcessGroupID generates an ID for a process group.
 //
-// This will return the pod name and the processGroupID ID.
-func GetProcessGroupID(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2.ProcessClass, idNum int) (string, string) {
-	var processGroupID string
+// This will return the pod name and the processGroupID.
+func GetProcessGroupID(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2.ProcessClass, idNum int) (string, fdbv1beta2.ProcessGroupID) {
+	var processGroupID fdbv1beta2.ProcessGroupID
 	if cluster.Spec.ProcessGroupIDPrefix != "" {
-		processGroupID = fmt.Sprintf("%s-%s-%d", cluster.Spec.ProcessGroupIDPrefix, processClass, idNum)
+		processGroupID = fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-%s-%d", cluster.Spec.ProcessGroupIDPrefix, processClass, idNum))
 	} else {
-		processGroupID = fmt.Sprintf("%s-%d", processClass, idNum)
+		processGroupID = fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-%d", processClass, idNum))
 	}
 	return fmt.Sprintf("%s-%s-%d", cluster.Name, processClassSanitizationPattern.ReplaceAllString(string(processClass), "-"), idNum), processGroupID
 }
@@ -107,7 +107,7 @@ func GetService(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2
 			Type:                     corev1.ServiceTypeClusterIP,
 			Ports:                    generateServicePorts(processesPerPod),
 			PublishNotReadyAddresses: true,
-			Selector:                 GetPodMatchLabels(cluster, "", id),
+			Selector:                 GetPodMatchLabels(cluster, "", string(id)),
 		},
 	}, nil
 }
@@ -194,7 +194,7 @@ func getContainers(podSpec *corev1.PodSpec) (*corev1.Container, *corev1.Containe
 	return mainContainer, sidecarContainer, nil
 }
 
-func configureContainersForUnifiedImages(cluster *fdbv1beta2.FoundationDBCluster, mainContainer *corev1.Container, sidecarContainer *corev1.Container, processGroupID string, processClass fdbv1beta2.ProcessClass) error {
+func configureContainersForUnifiedImages(cluster *fdbv1beta2.FoundationDBCluster, mainContainer *corev1.Container, sidecarContainer *corev1.Container, processGroupID fdbv1beta2.ProcessGroupID, processClass fdbv1beta2.ProcessClass) error {
 	mainContainer.Args = []string{
 		"--input-dir", "/var/dynamic-conf",
 		"--log-path", "/var/log/fdb-trace-logs/monitor.log",
@@ -368,7 +368,7 @@ func configureVolumesForContainers(cluster *fdbv1beta2.FoundationDBCluster, podS
 	podSpec.Volumes = append(podSpec.Volumes, volumes...)
 }
 
-func configureNoSchedule(podSpec *corev1.PodSpec, processGroupID string, noSchedules []string) {
+func configureNoSchedule(podSpec *corev1.PodSpec, processGroupID fdbv1beta2.ProcessGroupID, noSchedules []fdbv1beta2.ProcessGroupID) {
 	for _, noSchedulePID := range noSchedules {
 		if processGroupID != noSchedulePID {
 			continue
@@ -499,7 +499,7 @@ func GetPodSpec(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2
 
 // configureSidecarContainerForCluster sets up a sidecar container for a sidecar
 // in the FDB cluster.
-func configureSidecarContainerForCluster(cluster *fdbv1beta2.FoundationDBCluster, podName string, container *corev1.Container, initMode bool, processGroupID string) error {
+func configureSidecarContainerForCluster(cluster *fdbv1beta2.FoundationDBCluster, podName string, container *corev1.Container, initMode bool, processGroupID fdbv1beta2.ProcessGroupID) error {
 	return configureSidecarContainer(container, initMode, processGroupID, podName, cluster.GetRunningVersion(), cluster, cluster.Spec.SidecarContainer.ImageConfigs, false)
 }
 
@@ -510,7 +510,7 @@ func configureSidecarContainerForBackup(backup *fdbv1beta2.FoundationDBBackup, c
 }
 
 // configureSidecarContainer sets up a foundationdb-kubernetes-sidecar container.
-func configureSidecarContainer(container *corev1.Container, initMode bool, processGroupID string, podName string, versionString string, optionalCluster *fdbv1beta2.FoundationDBCluster, imageConfigs []fdbv1beta2.ImageConfig, allowTagOverride bool) error {
+func configureSidecarContainer(container *corev1.Container, initMode bool, processGroupID fdbv1beta2.ProcessGroupID, podName string, versionString string, optionalCluster *fdbv1beta2.FoundationDBCluster, imageConfigs []fdbv1beta2.ImageConfig, allowTagOverride bool) error {
 	sidecarEnv := make([]corev1.EnvVar, 0, 4)
 
 	hasTrustedCAs := optionalCluster != nil && len(optionalCluster.Spec.TrustedCAs) > 0
@@ -652,7 +652,7 @@ func configureSidecarContainer(container *corev1.Container, initMode bool, proce
 
 // getEnvForMonitorConfigSubstitution provides the environment variables that
 // are used for substituting variables into the monitor config.
-func getEnvForMonitorConfigSubstitution(cluster *fdbv1beta2.FoundationDBCluster, instanceID string) []corev1.EnvVar {
+func getEnvForMonitorConfigSubstitution(cluster *fdbv1beta2.FoundationDBCluster, instanceID fdbv1beta2.ProcessGroupID) []corev1.EnvVar {
 	env := make([]corev1.EnvVar, 0)
 
 	publicIPSource := cluster.Spec.Routing.PublicIPSource
@@ -719,7 +719,7 @@ func getEnvForMonitorConfigSubstitution(cluster *fdbv1beta2.FoundationDBCluster,
 		}
 	}
 
-	env = append(env, corev1.EnvVar{Name: "FDB_INSTANCE_ID", Value: instanceID})
+	env = append(env, corev1.EnvVar{Name: "FDB_INSTANCE_ID", Value: string(instanceID)})
 
 	return env
 }
@@ -988,7 +988,7 @@ func GetStorageServersPerPodForPod(pod *corev1.Pod) (int, error) {
 }
 
 // GetPodMetadata returns the metadata for a specific Pod
-func GetPodMetadata(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2.ProcessClass, id string, specHash string) metav1.ObjectMeta {
+func GetPodMetadata(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1beta2.ProcessClass, id fdbv1beta2.ProcessGroupID, specHash string) metav1.ObjectMeta {
 	var customMetadata *metav1.ObjectMeta
 
 	processSettings := cluster.GetProcessSettings(processClass)
@@ -1010,7 +1010,7 @@ func GetPodMetadata(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1b
 }
 
 // GetObjectMetadata returns the ObjectMetadata for a process
-func GetObjectMetadata(cluster *fdbv1beta2.FoundationDBCluster, base *metav1.ObjectMeta, processClass fdbv1beta2.ProcessClass, id string) metav1.ObjectMeta {
+func GetObjectMetadata(cluster *fdbv1beta2.FoundationDBCluster, base *metav1.ObjectMeta, processClass fdbv1beta2.ProcessClass, id fdbv1beta2.ProcessGroupID) metav1.ObjectMeta {
 	var metadata *metav1.ObjectMeta
 
 	if base != nil {
@@ -1024,7 +1024,7 @@ func GetObjectMetadata(cluster *fdbv1beta2.FoundationDBCluster, base *metav1.Obj
 		metadata.Labels = make(map[string]string)
 	}
 
-	for label, value := range GetPodLabels(cluster, processClass, id) {
+	for label, value := range GetPodLabels(cluster, processClass, string(id)) {
 		metadata.Labels[label] = value
 	}
 
diff --git a/internal/pvc_helper.go b/internal/pvc_helper.go
index 3887187f..afe2fe0f 100644
--- a/internal/pvc_helper.go
+++ b/internal/pvc_helper.go
@@ -26,8 +26,8 @@ import (
 )
 
 // CreatePVCMap creates a map with the process group ID as a key and the according PVC as a value
-func CreatePVCMap(cluster *fdbv1beta2.FoundationDBCluster, pvcs *corev1.PersistentVolumeClaimList) map[string]corev1.PersistentVolumeClaim {
-	pvcMap := make(map[string]corev1.PersistentVolumeClaim, len(pvcs.Items))
+func CreatePVCMap(cluster *fdbv1beta2.FoundationDBCluster, pvcs *corev1.PersistentVolumeClaimList) map[fdbv1beta2.ProcessGroupID]corev1.PersistentVolumeClaim {
+	pvcMap := make(map[fdbv1beta2.ProcessGroupID]corev1.PersistentVolumeClaim, len(pvcs.Items))
 	for _, pvc := range pvcs.Items {
 		processGroupID := GetProcessGroupIDFromMeta(cluster, pvc.ObjectMeta)
 		if processGroupID == "" {
diff --git a/internal/removals/remove.go b/internal/removals/remove.go
index 5f42b115..89ad7d06 100644
--- a/internal/removals/remove.go
+++ b/internal/removals/remove.go
@@ -39,9 +39,9 @@ const (
 )
 
 // GetProcessGroupsToRemove returns a list of process groups to be removed based on the removal mode.
-func GetProcessGroupsToRemove(removalMode fdbv1beta2.PodUpdateMode, removals map[string][]string) (string, []string, error) {
+func GetProcessGroupsToRemove(removalMode fdbv1beta2.PodUpdateMode, removals map[string][]fdbv1beta2.ProcessGroupID) (string, []fdbv1beta2.ProcessGroupID, error) {
 	if removalMode == fdbv1beta2.PodUpdateModeAll {
-		var deletions []string
+		var deletions []fdbv1beta2.ProcessGroupID
 
 		for _, zoneProcesses := range removals {
 			deletions = append(deletions, zoneProcesses...)
@@ -57,7 +57,7 @@ func GetProcessGroupsToRemove(removalMode fdbv1beta2.PodUpdateMode, removals map
 			}
 
 			// Fetch the first process group and delete it
-			return zoneProcesses[0], []string{zoneProcesses[0]}, nil
+			return string(zoneProcesses[0]), []fdbv1beta2.ProcessGroupID{zoneProcesses[0]}, nil
 		}
 	}
 
@@ -82,15 +82,15 @@ func GetProcessGroupsToRemove(removalMode fdbv1beta2.PodUpdateMode, removals map
 // GetZonedRemovals returns a map with the zone as key and a list of process groups IDs to be removed.
 // If the process group has not an associated process in the cluster status the zone will be UnknownZone.
 // if the process group has the ResourcesTerminating condition the zone will be TerminatingZone.
-func GetZonedRemovals(status *fdbv1beta2.FoundationDBStatus, processGroupsToRemove []*fdbv1beta2.ProcessGroupStatus) (map[string][]string, int64, error) {
+func GetZonedRemovals(status *fdbv1beta2.FoundationDBStatus, processGroupsToRemove []*fdbv1beta2.ProcessGroupStatus) (map[string][]fdbv1beta2.ProcessGroupID, int64, error) {
 	var lastestRemovalTimestamp int64
 	// Convert the process list into a map with the process group ID as key.
-	processInfo := map[string]fdbv1beta2.FoundationDBStatusProcessInfo{}
+	processInfo := map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{}
 	for _, p := range status.Cluster.Processes {
-		processInfo[p.Locality[fdbv1beta2.FDBLocalityInstanceIDKey]] = p
+		processInfo[fdbv1beta2.ProcessGroupID(p.Locality[fdbv1beta2.FDBLocalityInstanceIDKey])] = p
 	}
 
-	zoneMap := map[string][]string{}
+	zoneMap := map[string][]fdbv1beta2.ProcessGroupID{}
 	for _, pg := range processGroupsToRemove {
 		// Using the ResourcesTerminating is not a complete precise measurement of the time when we
 		// actually removed the process group, but it should be a good indicator to how long the process group is in
diff --git a/internal/replacements/replacements.go b/internal/replacements/replacements.go
index d2480ca6..733da729 100644
--- a/internal/replacements/replacements.go
+++ b/internal/replacements/replacements.go
@@ -33,7 +33,7 @@ import (
 )
 
 // ReplaceMisconfiguredProcessGroups checks if the cluster has any misconfigured process groups that must be replaced.
-func ReplaceMisconfiguredProcessGroups(log logr.Logger, cluster *fdbv1beta2.FoundationDBCluster, pvcMap map[string]corev1.PersistentVolumeClaim, podMap map[string]*corev1.Pod) (bool, error) {
+func ReplaceMisconfiguredProcessGroups(log logr.Logger, cluster *fdbv1beta2.FoundationDBCluster, pvcMap map[fdbv1beta2.ProcessGroupID]corev1.PersistentVolumeClaim, podMap map[fdbv1beta2.ProcessGroupID]*corev1.Pod) (bool, error) {
 	hasReplacements := false
 
 	maxReplacements := getMaxReplacements(cluster, cluster.GetMaxConcurrentReplacements())
diff --git a/kubectl-fdb/cmd/analyze.go b/kubectl-fdb/cmd/analyze.go
index 253faf8c..cf79b559 100644
--- a/kubectl-fdb/cmd/analyze.go
+++ b/kubectl-fdb/cmd/analyze.go
@@ -251,8 +251,8 @@ func analyzeCluster(cmd *cobra.Command, kubeClient client.Client, cluster *fdbv1
 
 	// We could add here more fields from cluster.Status.Generations and check if they are present.
 	var failedProcessGroups []string
-	processGroupMap := map[string]fdbv1beta2.None{}
-	removedProcessGroups := map[string]fdbv1beta2.None{}
+	processGroupMap := map[fdbv1beta2.ProcessGroupID]fdbv1beta2.None{}
+	removedProcessGroups := map[fdbv1beta2.ProcessGroupID]fdbv1beta2.None{}
 
 	// 3. Check for issues in processGroupID
 	processGroupIssue := false
@@ -297,7 +297,7 @@ func analyzeCluster(cmd *cobra.Command, kubeClient client.Client, cluster *fdbv1
 
 		needsReplacement, _ := processGroup.NeedsReplacement(0)
 		if needsReplacement && autoFix {
-			failedProcessGroups = append(failedProcessGroups, processGroup.ProcessGroupID)
+			failedProcessGroups = append(failedProcessGroups, string(processGroup.ProcessGroupID))
 		}
 	}
 
@@ -331,7 +331,7 @@ func analyzeCluster(cmd *cobra.Command, kubeClient client.Client, cluster *fdbv1
 	processGroupIDLabel := cluster.GetProcessGroupIDLabel()
 	for _, pod := range pods.Items {
 		// Skip Pods that are marked for removal those will probably be in a terminating state.
-		if _, ok := removedProcessGroups[pod.Labels[cluster.GetProcessGroupIDLabel()]]; ok {
+		if _, ok := removedProcessGroups[fdbv1beta2.ProcessGroupID(pod.Labels[cluster.GetProcessGroupIDLabel()])]; ok {
 			continue
 		}
 
@@ -374,7 +374,7 @@ func analyzeCluster(cmd *cobra.Command, kubeClient client.Client, cluster *fdbv1
 			killPods = append(killPods, pod)
 		}
 
-		id := pod.Labels[processGroupIDLabel]
+		id := fdbv1beta2.ProcessGroupID(pod.Labels[processGroupIDLabel])
 		if _, ok := processGroupMap[id]; !ok {
 			podIssue = true
 			statement := fmt.Sprintf("Pod %s/%s with the ID %s is not part of the cluster spec status", pod.Namespace, pod.Name, id)
diff --git a/kubectl-fdb/cmd/k8s_client.go b/kubectl-fdb/cmd/k8s_client.go
index 1d951c82..2bd82e8d 100644
--- a/kubectl-fdb/cmd/k8s_client.go
+++ b/kubectl-fdb/cmd/k8s_client.go
@@ -166,7 +166,7 @@ func getAllPodsFromClusterWithCondition(kubeClient client.Client, clusterName st
 		return []string{}, err
 	}
 
-	processesSet := make(map[string]bool)
+	processesSet := make(map[fdbv1beta2.ProcessGroupID]bool)
 
 	for _, condition := range conditions {
 		for _, process := range fdbv1beta2.FilterByCondition(cluster.Status.ProcessGroups, condition, true) {
@@ -178,10 +178,10 @@ func getAllPodsFromClusterWithCondition(kubeClient client.Client, clusterName st
 		}
 	}
 
-	processes := make([]string, 0, len(processesSet))
+	podNames := make([]string, 0, len(processesSet))
 	pods, err := getPodsForCluster(kubeClient, cluster)
 	if err != nil {
-		return processes, err
+		return podNames, err
 	}
 
 	for _, pod := range pods.Items {
@@ -190,20 +190,20 @@ func getAllPodsFromClusterWithCondition(kubeClient client.Client, clusterName st
 				continue
 			}
 
-			if pod.Labels[cluster.GetProcessGroupIDLabel()] != process {
+			if pod.Labels[cluster.GetProcessGroupIDLabel()] != string(process) {
 				continue
 			}
 
-			processes = append(processes, pod.Name)
+			podNames = append(podNames, pod.Name)
 		}
 	}
 
-	return processes, nil
+	return podNames, nil
 }
 
 // getProcessGroupIDsFromPodName returns the process group IDs based on the cluster configuration.
-func getProcessGroupIDsFromPodName(cluster *fdbv1beta2.FoundationDBCluster, podNames []string) ([]string, error) {
-	processGroupIDs := make([]string, 0, len(podNames))
+func getProcessGroupIDsFromPodName(cluster *fdbv1beta2.FoundationDBCluster, podNames []string) ([]fdbv1beta2.ProcessGroupID, error) {
+	processGroupIDs := make([]fdbv1beta2.ProcessGroupID, 0, len(podNames))
 
 	// TODO(johscheuer): We could validate if the provided process group is actually part of the cluster
 	for _, podName := range podNames {
diff --git a/kubectl-fdb/cmd/remove_process_group.go b/kubectl-fdb/cmd/remove_process_group.go
index ebace61d..03822030 100644
--- a/kubectl-fdb/cmd/remove_process_group.go
+++ b/kubectl-fdb/cmd/remove_process_group.go
@@ -80,7 +80,7 @@ func newRemoveProcessGroupCmd(streams genericclioptions.IOStreams) *cobra.Comman
 		},
 		Example: `
 # Remove process groups for a cluster in the current namespace
-kubectl fdb remove process-group -c cluster pod-1 -i pod-2
+kubectl fdb remove process-group -c cluster pod-1 pod-2
 
 # Remove process groups for a cluster in the namespace default
 kubectl fdb -n default remove process-group -c cluster pod-1 pod-2
@@ -112,8 +112,8 @@ kubectl fdb -n default remove process-group -c cluster --remove-all-failed
 }
 
 // replaceProcessGroups adds process groups to the removal list of the cluster
-func replaceProcessGroups(kubeClient client.Client, clusterName string, processGroupIDs []string, namespace string, withExclusion bool, wait bool, removeAllFailed bool, useProcessGroupID bool, sleep uint16) error {
-	if len(processGroupIDs) == 0 && !removeAllFailed {
+func replaceProcessGroups(kubeClient client.Client, clusterName string, ids []string, namespace string, withExclusion bool, wait bool, removeAllFailed bool, useProcessGroupID bool, sleep uint16) error {
+	if len(ids) == 0 && !removeAllFailed {
 		return nil
 	}
 
@@ -127,16 +127,21 @@ func replaceProcessGroups(kubeClient client.Client, clusterName string, processG
 	}
 
 	// In this case the user has Pod name specified
+	var processGroupIDs []fdbv1beta2.ProcessGroupID
 	if !useProcessGroupID {
-		processGroupIDs, err = getProcessGroupIDsFromPodName(cluster, processGroupIDs)
+		processGroupIDs, err = getProcessGroupIDsFromPodName(cluster, ids)
 		if err != nil {
 			return err
 		}
+	} else {
+		for _, id := range ids {
+			processGroupIDs = append(processGroupIDs, fdbv1beta2.ProcessGroupID(id))
+		}
 	}
 
 	patch := client.MergeFrom(cluster.DeepCopy())
 
-	processGroupSet := map[string]fdbv1beta2.None{}
+	processGroupSet := map[fdbv1beta2.ProcessGroupID]fdbv1beta2.None{}
 	for _, processGroup := range processGroupIDs {
 		processGroupSet[processGroup] = fdbv1beta2.None{}
 	}
@@ -163,7 +168,7 @@ func replaceProcessGroups(kubeClient client.Client, clusterName string, processG
 
 	if sleep > 0 {
 		for _, processGroupID := range processGroupIDs {
-			addProcessGroups([]string{processGroupID}, withExclusion, cluster)
+			addProcessGroups([]fdbv1beta2.ProcessGroupID{processGroupID}, withExclusion, cluster)
 			time.Sleep(time.Duration(sleep) * time.Second)
 		}
 	} else {
@@ -173,7 +178,7 @@ func replaceProcessGroups(kubeClient client.Client, clusterName string, processG
 	return kubeClient.Patch(ctx.TODO(), cluster, patch)
 }
 
-func addProcessGroups(processGroupIDs []string, withExclusion bool, cluster *fdbv1beta2.FoundationDBCluster) {
+func addProcessGroups(processGroupIDs []fdbv1beta2.ProcessGroupID, withExclusion bool, cluster *fdbv1beta2.FoundationDBCluster) {
 	if withExclusion {
 		cluster.AddProcessGroupsToRemovalList(processGroupIDs)
 	} else {
diff --git a/kubectl-fdb/cmd/root.go b/kubectl-fdb/cmd/root.go
index ed0e94c4..b3624c72 100644
--- a/kubectl-fdb/cmd/root.go
+++ b/kubectl-fdb/cmd/root.go
@@ -23,12 +23,13 @@ package cmd
 import (
 	"bufio"
 	"fmt"
-	"github.com/fatih/color"
 	"log"
 	"math/rand"
 	"os"
 	"time"
 
+	"github.com/fatih/color"
+
 	"strings"
 
 	"github.com/spf13/viper"
@@ -94,7 +95,7 @@ func NewRootCmd(streams genericclioptions.IOStreams) *cobra.Command {
 	return cmd
 }
 
-// confirmAction requests a user to confirm it's action
+// confirmAction requests a user to confirm its action
 func confirmAction(action string) bool {
 	reader := bufio.NewReader(os.Stdin)
 
diff --git a/pkg/fdbadminclient/lock_client.go b/pkg/fdbadminclient/lock_client.go
index a1425286..fb7e1116 100644
--- a/pkg/fdbadminclient/lock_client.go
+++ b/pkg/fdbadminclient/lock_client.go
@@ -34,11 +34,11 @@ type LockClient interface {
 
 	// AddPendingUpgrades registers information about which process groups are
 	// pending an upgrade to a new version.
-	AddPendingUpgrades(version fdbv1beta2.Version, processGroupIDs []string) error
+	AddPendingUpgrades(version fdbv1beta2.Version, processGroupIDs []fdbv1beta2.ProcessGroupID) error
 
 	// GetPendingUpgrades returns the stored information about which process
 	// groups are pending an upgrade to a new version.
-	GetPendingUpgrades(version fdbv1beta2.Version) (map[string]bool, error)
+	GetPendingUpgrades(version fdbv1beta2.Version) (map[fdbv1beta2.ProcessGroupID]bool, error)
 
 	// ClearPendingUpgrades clears any stored information about pending
 	// upgrades.
diff --git a/pkg/fdbadminclient/mock/admin_client_mock.go b/pkg/fdbadminclient/mock/admin_client_mock.go
index ca6a3a8f..b3aa8290 100644
--- a/pkg/fdbadminclient/mock/admin_client_mock.go
+++ b/pkg/fdbadminclient/mock/admin_client_mock.go
@@ -51,12 +51,12 @@ type AdminClient struct {
 	Backups                                  map[string]fdbv1beta2.FoundationDBBackupStatusBackupDetails
 	clientVersions                           map[string][]string
 	currentCommandLines                      map[string]string
-	VersionProcessGroups                     map[string]string
-	missingProcessGroups                     map[string]bool
-	incorrectCommandLines                    map[string]bool
+	VersionProcessGroups                     map[fdbv1beta2.ProcessGroupID]string
+	missingProcessGroups                     map[fdbv1beta2.ProcessGroupID]bool
+	incorrectCommandLines                    map[fdbv1beta2.ProcessGroupID]bool
 	ReincludedAddresses                      map[string]bool
 	additionalProcesses                      []fdbv1beta2.ProcessGroupStatus
-	localityInfo                             map[string]map[string]string
+	localityInfo                             map[fdbv1beta2.ProcessGroupID]map[string]string
 	MaxZoneFailuresWithoutLosingData         *int
 	MaxZoneFailuresWithoutLosingAvailability *int
 	MaintenanceZone                          string
@@ -90,11 +90,11 @@ func NewMockAdminClientUncast(cluster *fdbv1beta2.FoundationDBCluster, kubeClien
 			ExcludedAddresses:    make(map[string]fdbv1beta2.None),
 			ReincludedAddresses:  make(map[string]bool),
 			KilledAddresses:      make(map[string]fdbv1beta2.None),
-			missingProcessGroups: make(map[string]bool),
-			localityInfo:         make(map[string]map[string]string),
+			missingProcessGroups: make(map[fdbv1beta2.ProcessGroupID]bool),
+			localityInfo:         make(map[fdbv1beta2.ProcessGroupID]map[string]string),
 			currentCommandLines:  make(map[string]string),
 			Knobs:                make(map[string]fdbv1beta2.None),
-			VersionProcessGroups: make(map[string]string),
+			VersionProcessGroups: make(map[fdbv1beta2.ProcessGroupID]string),
 		}
 		adminClientCache[cluster.Name] = cachedClient
 		cachedClient.Backups = make(map[string]fdbv1beta2.FoundationDBBackupStatusBackupDetails)
@@ -125,7 +125,7 @@ func (client *AdminClient) GetStatus() (*fdbv1beta2.FoundationDBStatus, error) {
 	}
 	status := &fdbv1beta2.FoundationDBStatus{
 		Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-			Processes: make(map[string]fdbv1beta2.FoundationDBStatusProcessInfo, len(pods.Items)),
+			Processes: make(map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo, len(pods.Items)),
 		},
 	}
 
@@ -200,7 +200,7 @@ func (client *AdminClient) GetStatus() (*fdbv1beta2.FoundationDBStatus, error) {
 			}
 
 			locality := map[string]string{
-				fdbv1beta2.FDBLocalityInstanceIDKey: processGroupID,
+				fdbv1beta2.FDBLocalityInstanceIDKey: string(processGroupID),
 				fdbv1beta2.FDBLocalityZoneIDKey:     pod.Name,
 				fdbv1beta2.FDBLocalityDCIDKey:       client.Cluster.Spec.DataCenter,
 			}
@@ -230,12 +230,12 @@ func (client *AdminClient) GetStatus() (*fdbv1beta2.FoundationDBStatus, error) {
 				}
 			}
 
-			version, ok := client.VersionProcessGroups[locality[fdbv1beta2.FDBLocalityProcessIDKey]]
+			version, ok := client.VersionProcessGroups[fdbv1beta2.ProcessGroupID(locality[fdbv1beta2.FDBLocalityProcessIDKey])]
 			if !ok {
 				version = client.Cluster.Status.RunningVersion
 			}
 
-			status.Cluster.Processes[fmt.Sprintf("%s-%d", pod.Name, processIndex)] = fdbv1beta2.FoundationDBStatusProcessInfo{
+			status.Cluster.Processes[fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-%d", pod.Name, processIndex))] = fdbv1beta2.FoundationDBStatusProcessInfo{
 				Address:       fullAddress,
 				ProcessClass:  internal.GetProcessClassFromMeta(client.Cluster, pod.ObjectMeta),
 				CommandLine:   command,
@@ -249,8 +249,8 @@ func (client *AdminClient) GetStatus() (*fdbv1beta2.FoundationDBStatus, error) {
 
 		for _, processGroup := range client.additionalProcesses {
 			locality := map[string]string{
-				fdbv1beta2.FDBLocalityInstanceIDKey: processGroup.ProcessGroupID,
-				fdbv1beta2.FDBLocalityZoneIDKey:     processGroup.ProcessGroupID,
+				fdbv1beta2.FDBLocalityInstanceIDKey: string(processGroup.ProcessGroupID),
+				fdbv1beta2.FDBLocalityZoneIDKey:     string(processGroup.ProcessGroupID),
 			}
 
 			for key, value := range client.localityInfo[processGroupID] {
@@ -258,7 +258,7 @@ func (client *AdminClient) GetStatus() (*fdbv1beta2.FoundationDBStatus, error) {
 			}
 
 			var uptimeSeconds float64 = 60000
-			if client.MaintenanceZone == processGroup.ProcessGroupID || client.MaintenanceZone == "simulation" {
+			if client.MaintenanceZone == string(processGroup.ProcessGroupID) || client.MaintenanceZone == "simulation" {
 				if client.uptimeSecondsForMaintenanceZone != 0.0 {
 					uptimeSeconds = client.uptimeSecondsForMaintenanceZone
 				} else {
@@ -661,20 +661,20 @@ func (client *AdminClient) MockAdditionalProcesses(processes []fdbv1beta2.Proces
 
 // MockMissingProcessGroup updates the mock for whether a process group should
 // be missing from the cluster status.
-func (client *AdminClient) MockMissingProcessGroup(processGroupID string, missing bool) {
+func (client *AdminClient) MockMissingProcessGroup(processGroupID fdbv1beta2.ProcessGroupID, missing bool) {
 	client.missingProcessGroups[processGroupID] = missing
 }
 
 // MockLocalityInfo sets mock locality information for a process.
-func (client *AdminClient) MockLocalityInfo(processGroupID string, locality map[string]string) {
+func (client *AdminClient) MockLocalityInfo(processGroupID fdbv1beta2.ProcessGroupID, locality map[string]string) {
 	client.localityInfo[processGroupID] = locality
 }
 
 // MockIncorrectCommandLine updates the mock for whether a process group should
 // be have an incorrect command-line.
-func (client *AdminClient) MockIncorrectCommandLine(processGroupID string, incorrect bool) {
+func (client *AdminClient) MockIncorrectCommandLine(processGroupID fdbv1beta2.ProcessGroupID, incorrect bool) {
 	if client.incorrectCommandLines == nil {
-		client.incorrectCommandLines = make(map[string]bool)
+		client.incorrectCommandLines = make(map[fdbv1beta2.ProcessGroupID]bool)
 	}
 	client.incorrectCommandLines[processGroupID] = incorrect
 }
diff --git a/pkg/fdbadminclient/mock/lock_client.go b/pkg/fdbadminclient/mock/lock_client.go
index d7c3fc7a..787b9825 100644
--- a/pkg/fdbadminclient/mock/lock_client.go
+++ b/pkg/fdbadminclient/mock/lock_client.go
@@ -39,7 +39,7 @@ type LockClient struct {
 
 	// pendingUpgrades stores data about process groups that have a pending
 	// upgrade.
-	pendingUpgrades map[fdbv1beta2.Version]map[string]bool
+	pendingUpgrades map[fdbv1beta2.Version]map[fdbv1beta2.ProcessGroupID]bool
 }
 
 // TakeLock attempts to acquire a lock.
@@ -54,9 +54,9 @@ func (client *LockClient) Disabled() bool {
 
 // AddPendingUpgrades registers information about which process groups are
 // pending an upgrade to a new version.
-func (client *LockClient) AddPendingUpgrades(version fdbv1beta2.Version, processGroupIDs []string) error {
+func (client *LockClient) AddPendingUpgrades(version fdbv1beta2.Version, processGroupIDs []fdbv1beta2.ProcessGroupID) error {
 	if client.pendingUpgrades[version] == nil {
-		client.pendingUpgrades[version] = make(map[string]bool)
+		client.pendingUpgrades[version] = make(map[fdbv1beta2.ProcessGroupID]bool)
 	}
 	for _, processGroupID := range processGroupIDs {
 		client.pendingUpgrades[version][processGroupID] = true
@@ -66,10 +66,10 @@ func (client *LockClient) AddPendingUpgrades(version fdbv1beta2.Version, process
 
 // GetPendingUpgrades returns the stored information about which process
 // groups are pending an upgrade to a new version.
-func (client *LockClient) GetPendingUpgrades(version fdbv1beta2.Version) (map[string]bool, error) {
+func (client *LockClient) GetPendingUpgrades(version fdbv1beta2.Version) (map[fdbv1beta2.ProcessGroupID]bool, error) {
 	upgrades := client.pendingUpgrades[version]
 	if upgrades == nil {
-		return make(map[string]bool), nil
+		return make(map[fdbv1beta2.ProcessGroupID]bool), nil
 	}
 	return upgrades, nil
 }
@@ -123,7 +123,7 @@ func NewMockLockClientUncast(cluster *fdbv1beta2.FoundationDBCluster) *LockClien
 
 	client := lockClientCache[cluster.Name]
 	if client == nil {
-		client = &LockClient{cluster: cluster, pendingUpgrades: make(map[fdbv1beta2.Version]map[string]bool)}
+		client = &LockClient{cluster: cluster, pendingUpgrades: make(map[fdbv1beta2.Version]map[fdbv1beta2.ProcessGroupID]bool)}
 		lockClientCache[cluster.Name] = client
 	}
 	return client
diff --git a/pkg/podmanager/fdb_process_group.go b/pkg/podmanager/fdb_process_group.go
index f5a98f81..9703cebe 100644
--- a/pkg/podmanager/fdb_process_group.go
+++ b/pkg/podmanager/fdb_process_group.go
@@ -34,7 +34,7 @@ import (
 var processIDRegex = regexp.MustCompile(`^([\w-]+-\d)-\d$`)
 
 // ParseProcessGroupID extracts the components of a process group ID.
-func ParseProcessGroupID(id string) (fdbv1beta2.ProcessClass, int, error) {
+func ParseProcessGroupID(id fdbv1beta2.ProcessGroupID) (fdbv1beta2.ProcessClass, int, error) {
 	return internal.ParseProcessGroupID(id)
 }
 
@@ -50,7 +50,7 @@ func GetProcessGroupIDFromProcessID(id string) string {
 }
 
 // GetProcessGroupID returns the process group ID from the Pods metadata
-func GetProcessGroupID(cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod) string {
+func GetProcessGroupID(cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod) fdbv1beta2.ProcessGroupID {
 	if pod == nil {
 		return ""
 	}


Test patch:
diff --git a/api/v1beta2/foundationdb_status_test.go b/api/v1beta2/foundationdb_status_test.go
index 2842f283..8a7c5f4c 100644
--- a/api/v1beta2/foundationdb_status_test.go
+++ b/api/v1beta2/foundationdb_status_test.go
@@ -85,7 +85,7 @@ var _ = Describe("FoundationDBStatus", func() {
 						RoleCounts:      RoleCounts{Storage: 0, Logs: 3, Proxies: 3, Resolvers: 1, LogRouters: 0, RemoteLogs: 0},
 						VersionFlags:    VersionFlags{LogSpill: 2},
 					},
-					Processes: map[string]FoundationDBStatusProcessInfo{
+					Processes: map[ProcessGroupID]FoundationDBStatusProcessInfo{
 						"b9c25278c0fa207bc2a73bda2300d0a9": {
 							Address: ProcessAddress{
 								IPAddress: net.ParseIP("10.1.38.93"),
@@ -482,7 +482,7 @@ var _ = Describe("FoundationDBStatus", func() {
 				RoleCounts:      RoleCounts{Storage: 0, Logs: 3, Proxies: 3, CommitProxies: 2, GrvProxies: 1, Resolvers: 1, LogRouters: -1, RemoteLogs: -1},
 				VersionFlags:    VersionFlags{LogSpill: 2, LogVersion: 0},
 			},
-			Processes: map[string]FoundationDBStatusProcessInfo{
+			Processes: map[ProcessGroupID]FoundationDBStatusProcessInfo{
 				"eb48ada3a682e86363f06aa89e1041fa": {
 					Address: ProcessAddress{
 						IPAddress: net.ParseIP("10.1.18.254"),
diff --git a/api/v1beta2/foundationdbcluster_types_test.go b/api/v1beta2/foundationdbcluster_types_test.go
index 1a55aee5..b0578306 100644
--- a/api/v1beta2/foundationdbcluster_types_test.go
+++ b/api/v1beta2/foundationdbcluster_types_test.go
@@ -2919,12 +2919,12 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 			}
 			Expect(cluster.ProcessGroupIsBeingRemoved("storage-1")).To(BeFalse())
 
-			cluster.Spec.ProcessGroupsToRemove = []string{"log-1"}
+			cluster.Spec.ProcessGroupsToRemove = []ProcessGroupID{"log-1"}
 			Expect(cluster.ProcessGroupIsBeingRemoved("storage-1")).To(BeFalse())
 			Expect(cluster.ProcessGroupIsBeingRemoved("log-1")).To(BeTrue())
 			cluster.Spec.ProcessGroupsToRemove = nil
 
-			cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = []string{"log-1"}
+			cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = []ProcessGroupID{"log-1"}
 			Expect(cluster.ProcessGroupIsBeingRemoved("storage-1")).To(BeFalse())
 			Expect(cluster.ProcessGroupIsBeingRemoved("log-1")).To(BeTrue())
 			cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = nil
@@ -3555,21 +3555,21 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 		Context("with a single condition", func() {
 			It("should return the process groups with that condition", func() {
 				groups := FilterByCondition(status, IncorrectCommandLine, false)
-				Expect(groups).To(Equal([]string{"storage-2", "storage-4", "storage-5"}))
+				Expect(groups).To(Equal([]ProcessGroupID{"storage-2", "storage-4", "storage-5"}))
 			})
 		})
 
 		When("ignoring removed processes", func() {
 			It("should return the process groups with that condition", func() {
 				groups := FilterByCondition(status, IncorrectCommandLine, true)
-				Expect(groups).To(Equal([]string{"storage-2", "storage-4"}))
+				Expect(groups).To(Equal([]ProcessGroupID{"storage-2", "storage-4"}))
 			})
 		})
 
 		Context("with a mix of required and forbidden conditions", func() {
 			It("should return the process groups that match all rules", func() {
 				groups := FilterByConditions(status, map[ProcessGroupConditionType]bool{IncorrectCommandLine: true, IncorrectPodSpec: false}, false)
-				Expect(groups).To(Equal([]string{"storage-2", "storage-5"}))
+				Expect(groups).To(Equal([]ProcessGroupID{"storage-2", "storage-5"}))
 			})
 		})
 	})
@@ -4448,7 +4448,7 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 		When("removing with exclusion", func() {
 			When("a np process group is already included in the list", func() {
 				It("should add the process group to the removal list", func() {
-					removals := []string{"test1"}
+					removals := []ProcessGroupID{"test1"}
 					cluster.AddProcessGroupsToRemovalList(removals)
 					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements(removals))
 					Expect(len(cluster.Spec.ProcessGroupsToRemove)).To(Equal(len(removals)))
@@ -4462,8 +4462,8 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				})
 
 				It("should only add the missing process groups", func() {
-					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements("test1"))
-					removals := []string{"test1", "test2"}
+					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements(ProcessGroupID("test1")))
+					removals := []ProcessGroupID{"test1", "test2"}
 					cluster.AddProcessGroupsToRemovalList(removals)
 					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements(removals))
 					Expect(len(cluster.Spec.ProcessGroupsToRemove)).To(Equal(len(removals)))
@@ -4475,7 +4475,7 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 		When("removing without exclusion", func() {
 			When("a process group is not already included in the list", func() {
 				It("should add the process group to the removal list", func() {
-					removals := []string{"test1"}
+					removals := []ProcessGroupID{"test1"}
 					cluster.AddProcessGroupsToRemovalWithoutExclusionList(removals)
 					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(removals))
 					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(len(removals)))
@@ -4486,11 +4486,11 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 			When("a process group is already included in the list", func() {
 				BeforeEach(func() {
 					cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = append(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion, "test1")
-					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements("test1"))
+					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(ProcessGroupID("test1")))
 				})
 
 				It("should only add the missing process groups", func() {
-					removals := []string{"test1", "test2"}
+					removals := []ProcessGroupID{"test1", "test2"}
 					cluster.AddProcessGroupsToRemovalWithoutExclusionList(removals)
 					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(removals))
 					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(len(removals)))
@@ -4501,12 +4501,12 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 			When("a process group is already included in the with exclusion list", func() {
 				BeforeEach(func() {
 					cluster.Spec.ProcessGroupsToRemove = append(cluster.Spec.ProcessGroupsToRemove, "test1")
-					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements("test1"))
+					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements(ProcessGroupID("test1")))
 					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(0))
 				})
 
 				It("should only add the missing process groups", func() {
-					removals := []string{"test1", "test2"}
+					removals := []ProcessGroupID{"test1", "test2"}
 					cluster.AddProcessGroupsToRemovalWithoutExclusionList(removals)
 					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(removals))
 					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(len(removals)))
@@ -4662,8 +4662,8 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 
 		When("the no-schedule list is empty", func() {
 			type testCase struct {
-				Instances                     []string
-				ExpectedInstancesInNoSchedule []string
+				Instances                     []ProcessGroupID
+				ExpectedInstancesInNoSchedule []ProcessGroupID
 			}
 
 			DescribeTable("should add all targeted processes to the no-schedule list",
@@ -4674,25 +4674,25 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				},
 				Entry("Adding single instance",
 					testCase{
-						Instances:                     []string{"instance-1"},
-						ExpectedInstancesInNoSchedule: []string{"instance-1"},
+						Instances:                     []ProcessGroupID{"instance-1"},
+						ExpectedInstancesInNoSchedule: []ProcessGroupID{"instance-1"},
 					}),
 				Entry("Adding multiple instances",
 					testCase{
-						Instances:                     []string{"instance-1", "instance-2"},
-						ExpectedInstancesInNoSchedule: []string{"instance-1", "instance-2"},
+						Instances:                     []ProcessGroupID{"instance-1", "instance-2"},
+						ExpectedInstancesInNoSchedule: []ProcessGroupID{"instance-1", "instance-2"},
 					}),
 			)
 		})
 
 		When("the no-schedule list is not empty", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.NoSchedule = []string{"instance-1"}
+				cluster.Spec.Buggify.NoSchedule = []ProcessGroupID{"instance-1"}
 			})
 
 			type testCase struct {
-				Instances                     []string
-				ExpectedInstancesInNoSchedule []string
+				Instances                     []ProcessGroupID
+				ExpectedInstancesInNoSchedule []ProcessGroupID
 			}
 
 			DescribeTable("should add all targeted processes to no-schedule list",
@@ -4703,13 +4703,13 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				},
 				Entry("Adding single instance",
 					testCase{
-						Instances:                     []string{"instance-2"},
-						ExpectedInstancesInNoSchedule: []string{"instance-1", "instance-2"},
+						Instances:                     []ProcessGroupID{"instance-2"},
+						ExpectedInstancesInNoSchedule: []ProcessGroupID{"instance-1", "instance-2"},
 					}),
 				Entry("Adding multiple instances",
 					testCase{
-						Instances:                     []string{"instance-2", "instance-3"},
-						ExpectedInstancesInNoSchedule: []string{"instance-1", "instance-2", "instance-3"},
+						Instances:                     []ProcessGroupID{"instance-2", "instance-3"},
+						ExpectedInstancesInNoSchedule: []ProcessGroupID{"instance-1", "instance-2", "instance-3"},
 					}),
 			)
 		})
@@ -4723,15 +4723,15 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 			cluster = &FoundationDBCluster{
 				Spec: FoundationDBClusterSpec{
 					Buggify: BuggifyConfig{
-						NoSchedule: []string{"instance-1", "instance-2", "instance-3"},
+						NoSchedule: []ProcessGroupID{"instance-1", "instance-2", "instance-3"},
 					},
 				},
 			}
 		})
 
 		type testCase struct {
-			Instances                         []string
-			ExpectedInstancesInNoScheduleList []string
+			Instances                         []ProcessGroupID
+			ExpectedInstancesInNoScheduleList []ProcessGroupID
 		}
 
 		DescribeTable("should remove all targeted processes from the no-schedule list",
@@ -4742,13 +4742,13 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 			},
 			Entry("Removing single instance",
 				testCase{
-					Instances:                         []string{"instance-1"},
-					ExpectedInstancesInNoScheduleList: []string{"instance-2", "instance-3"},
+					Instances:                         []ProcessGroupID{"instance-1"},
+					ExpectedInstancesInNoScheduleList: []ProcessGroupID{"instance-2", "instance-3"},
 				}),
 			Entry("Removing multiple instances",
 				testCase{
-					Instances:                         []string{"instance-2", "instance-3"},
-					ExpectedInstancesInNoScheduleList: []string{"instance-1"},
+					Instances:                         []ProcessGroupID{"instance-2", "instance-3"},
+					ExpectedInstancesInNoScheduleList: []ProcessGroupID{"instance-1"},
 				}),
 		)
 
@@ -4763,8 +4763,8 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 
 		When("the crash-loop list is empty", func() {
 			type testCase struct {
-				Instances                    []string
-				ExpectedInstancesInCrashLoop []string
+				Instances                    []ProcessGroupID
+				ExpectedInstancesInCrashLoop []ProcessGroupID
 			}
 
 			DescribeTable("should add all targeted processes to the crash-loop list",
@@ -4775,30 +4775,30 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				},
 				Entry("Adding single instance",
 					testCase{
-						Instances:                    []string{"instance-1"},
-						ExpectedInstancesInCrashLoop: []string{"instance-1"},
+						Instances:                    []ProcessGroupID{"instance-1"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-1"},
 					}),
 				Entry("Adding multiple instances",
 					testCase{
-						Instances:                    []string{"instance-1", "instance-2"},
-						ExpectedInstancesInCrashLoop: []string{"instance-1", "instance-2"},
+						Instances:                    []ProcessGroupID{"instance-1", "instance-2"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-1", "instance-2"},
 					}),
 				Entry("Adding all instances",
 					testCase{
-						Instances:                    []string{"*"},
-						ExpectedInstancesInCrashLoop: []string{"*"},
+						Instances:                    []ProcessGroupID{"*"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"*"},
 					}),
 			)
 		})
 
 		When("the crash-loop list is not empty", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"instance-1"}
+				cluster.Spec.Buggify.CrashLoop = []ProcessGroupID{"instance-1"}
 			})
 
 			type testCase struct {
-				Instances                    []string
-				ExpectedInstancesInCrashLoop []string
+				Instances                    []ProcessGroupID
+				ExpectedInstancesInCrashLoop []ProcessGroupID
 			}
 
 			DescribeTable("should add all targeted processes to crash-loop list",
@@ -4809,30 +4809,30 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				},
 				Entry("Adding single instance",
 					testCase{
-						Instances:                    []string{"instance-2"},
-						ExpectedInstancesInCrashLoop: []string{"instance-1", "instance-2"},
+						Instances:                    []ProcessGroupID{"instance-2"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-1", "instance-2"},
 					}),
 				Entry("Adding multiple instances",
 					testCase{
-						Instances:                    []string{"instance-2", "instance-3"},
-						ExpectedInstancesInCrashLoop: []string{"instance-1", "instance-2", "instance-3"},
+						Instances:                    []ProcessGroupID{"instance-2", "instance-3"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-1", "instance-2", "instance-3"},
 					}),
 				Entry("Adding all instances",
 					testCase{
-						Instances:                    []string{"*"},
-						ExpectedInstancesInCrashLoop: []string{"instance-1", "*"},
+						Instances:                    []ProcessGroupID{"*"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-1", "*"},
 					}),
 			)
 		})
 
 		When("the crash-loop list contains *", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"*"}
+				cluster.Spec.Buggify.CrashLoop = []ProcessGroupID{"*"}
 			})
 
 			type testCase struct {
-				Instances                    []string
-				ExpectedInstancesInCrashLoop []string
+				Instances                    []ProcessGroupID
+				ExpectedInstancesInCrashLoop []ProcessGroupID
 			}
 
 			DescribeTable("should add all targeted processes to crash-loop list",
@@ -4843,18 +4843,18 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				},
 				Entry("Adding single instance",
 					testCase{
-						Instances:                    []string{"instance-1"},
-						ExpectedInstancesInCrashLoop: []string{"instance-1", "*"},
+						Instances:                    []ProcessGroupID{"instance-1"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-1", "*"},
 					}),
 				Entry("Adding multiple instances",
 					testCase{
-						Instances:                    []string{"instance-2", "instance-3"},
-						ExpectedInstancesInCrashLoop: []string{"*", "instance-2", "instance-3"},
+						Instances:                    []ProcessGroupID{"instance-2", "instance-3"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"*", "instance-2", "instance-3"},
 					}),
 				Entry("Adding all instances",
 					testCase{
-						Instances:                    []string{"*"},
-						ExpectedInstancesInCrashLoop: []string{"*"},
+						Instances:                    []ProcessGroupID{"*"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"*"},
 					}),
 			)
 		})
@@ -4869,15 +4869,15 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				cluster = &FoundationDBCluster{
 					Spec: FoundationDBClusterSpec{
 						Buggify: BuggifyConfig{
-							CrashLoop: []string{"instance-1", "instance-2", "instance-3"},
+							CrashLoop: []ProcessGroupID{"instance-1", "instance-2", "instance-3"},
 						},
 					},
 				}
 			})
 
 			type testCase struct {
-				Instances                    []string
-				ExpectedInstancesInCrashLoop []string
+				Instances                    []ProcessGroupID
+				ExpectedInstancesInCrashLoop []ProcessGroupID
 			}
 
 			DescribeTable("should remove all targeted processes from the crash-loop list",
@@ -4888,13 +4888,13 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				},
 				Entry("Removing single instance",
 					testCase{
-						Instances:                    []string{"instance-1"},
-						ExpectedInstancesInCrashLoop: []string{"instance-2", "instance-3"},
+						Instances:                    []ProcessGroupID{"instance-1"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-2", "instance-3"},
 					}),
 				Entry("Removing multiple instances",
 					testCase{
-						Instances:                    []string{"instance-2", "instance-3"},
-						ExpectedInstancesInCrashLoop: []string{"instance-1"},
+						Instances:                    []ProcessGroupID{"instance-2", "instance-3"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-1"},
 					}),
 			)
 		})
@@ -4904,15 +4904,15 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				cluster = &FoundationDBCluster{
 					Spec: FoundationDBClusterSpec{
 						Buggify: BuggifyConfig{
-							CrashLoop: []string{"*", "instance-1", "instance-2", "instance-3"},
+							CrashLoop: []ProcessGroupID{"*", "instance-1", "instance-2", "instance-3"},
 						},
 					},
 				}
 			})
 
 			type testCase struct {
-				Instances                    []string
-				ExpectedInstancesInCrashLoop []string
+				Instances                    []ProcessGroupID
+				ExpectedInstancesInCrashLoop []ProcessGroupID
 			}
 
 			DescribeTable("should remove all targeted processes from the crash-loop list",
@@ -4923,18 +4923,18 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				},
 				Entry("Removing single instance",
 					testCase{
-						Instances:                    []string{"instance-1"},
-						ExpectedInstancesInCrashLoop: []string{"*", "instance-2", "instance-3"},
+						Instances:                    []ProcessGroupID{"instance-1"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"*", "instance-2", "instance-3"},
 					}),
 				Entry("Removing multiple instances",
 					testCase{
-						Instances:                    []string{"instance-2", "instance-3"},
-						ExpectedInstancesInCrashLoop: []string{"*", "instance-1"},
+						Instances:                    []ProcessGroupID{"instance-2", "instance-3"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"*", "instance-1"},
 					}),
 				Entry("Removing *",
 					testCase{
-						Instances:                    []string{"*"},
-						ExpectedInstancesInCrashLoop: []string{"instance-1", "instance-2", "instance-3"},
+						Instances:                    []ProcessGroupID{"*"},
+						ExpectedInstancesInCrashLoop: []ProcessGroupID{"instance-1", "instance-2", "instance-3"},
 					}),
 			)
 		})
diff --git a/controllers/add_process_groups_test.go b/controllers/add_process_groups_test.go
index 098811cc..c978102d 100644
--- a/controllers/add_process_groups_test.go
+++ b/controllers/add_process_groups_test.go
@@ -85,13 +85,13 @@ var _ = Describe("add_process_groups", func() {
 		})
 
 		It("should add a storage process", func() {
-			storageProcesses := make([]string, 0, newProcessCounts.Storage)
+			storageProcesses := make([]fdbv1beta2.ProcessGroupID, 0, newProcessCounts.Storage)
 			for _, processGroup := range cluster.Status.ProcessGroups {
 				if processGroup.ProcessClass == "storage" {
 					storageProcesses = append(storageProcesses, processGroup.ProcessGroupID)
 				}
 			}
-			expectedStorageProcesses := []string{
+			expectedStorageProcesses := []fdbv1beta2.ProcessGroupID{
 				"storage-1",
 				"storage-2",
 				"storage-3",
@@ -119,13 +119,13 @@ var _ = Describe("add_process_groups", func() {
 		})
 
 		It("should add a storage process", func() {
-			storageProcesses := make([]string, 0, newProcessCounts.Storage)
+			storageProcesses := make([]fdbv1beta2.ProcessGroupID, 0, newProcessCounts.Storage)
 			for _, processGroup := range cluster.Status.ProcessGroups {
 				if processGroup.ProcessClass == "storage" {
 					storageProcesses = append(storageProcesses, processGroup.ProcessGroupID)
 				}
 			}
-			expectedStorageProcesses := []string{
+			expectedStorageProcesses := []fdbv1beta2.ProcessGroupID{
 				"old-prefix-storage-4",
 				"storage-1",
 				"storage-2",
@@ -152,13 +152,13 @@ var _ = Describe("add_process_groups", func() {
 		})
 
 		It("should add storage processes", func() {
-			storageProcesses := make([]string, 0, newProcessCounts.Storage)
+			storageProcesses := make([]fdbv1beta2.ProcessGroupID, 0, newProcessCounts.Storage)
 			for _, processGroup := range cluster.Status.ProcessGroups {
 				if processGroup.ProcessClass == "storage" {
 					storageProcesses = append(storageProcesses, processGroup.ProcessGroupID)
 				}
 			}
-			expectedStorageProcesses := []string{
+			expectedStorageProcesses := []fdbv1beta2.ProcessGroupID{
 				"storage-1",
 				"storage-2",
 				"storage-3",
@@ -185,13 +185,13 @@ var _ = Describe("add_process_groups", func() {
 			})
 
 			It("should fill in the gap", func() {
-				storageProcesses := make([]string, 0, newProcessCounts.Storage)
+				storageProcesses := make([]fdbv1beta2.ProcessGroupID, 0, newProcessCounts.Storage)
 				for _, processGroup := range cluster.Status.ProcessGroups {
 					if processGroup.ProcessClass == "storage" {
 						storageProcesses = append(storageProcesses, processGroup.ProcessGroupID)
 					}
 				}
-				expectedStorageProcesses := []string{
+				expectedStorageProcesses := []fdbv1beta2.ProcessGroupID{
 					"storage-1",
 					"storage-2",
 					"storage-3",
diff --git a/controllers/bounce_processes_test.go b/controllers/bounce_processes_test.go
index 0b27dfb5..4fa19f74 100644
--- a/controllers/bounce_processes_test.go
+++ b/controllers/bounce_processes_test.go
@@ -71,11 +71,11 @@ var _ = Describe("bounceProcesses", func() {
 	Context("with incorrect processes", func() {
 		BeforeEach(func() {
 			processGroup := cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-4]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 
 			processGroup = cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-3]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-2"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 		})
 
@@ -85,7 +85,7 @@ var _ = Describe("bounceProcesses", func() {
 
 		It("should kill the targeted processes", func() {
 			addresses := make(map[string]fdbv1beta2.None, 2)
-			for _, processGroupID := range []string{"storage-1", "storage-2"} {
+			for _, processGroupID := range []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"} {
 				processGroupAddresses := fdbv1beta2.FindProcessGroupByID(cluster.Status.ProcessGroups, processGroupID).Addresses
 				for _, address := range processGroupAddresses {
 					addresses[fmt.Sprintf("%s:4501", address)] = fdbv1beta2.None{}
@@ -98,11 +98,11 @@ var _ = Describe("bounceProcesses", func() {
 	Context("with excluded and incorrect processes", func() {
 		BeforeEach(func() {
 			processGroup := cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-4]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 
 			processGroup = cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-3]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-2"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 			processGroup.MarkForRemoval()
 		})
@@ -113,7 +113,7 @@ var _ = Describe("bounceProcesses", func() {
 
 		It("should kill the targeted processes", func() {
 			addresses := make(map[string]fdbv1beta2.None, 1)
-			for _, processGroupID := range []string{"storage-1"} {
+			for _, processGroupID := range []fdbv1beta2.ProcessGroupID{"storage-1"} {
 				processGroupAddresses := fdbv1beta2.FindProcessGroupByID(cluster.Status.ProcessGroups, processGroupID).Addresses
 				for _, address := range processGroupAddresses {
 					addresses[fmt.Sprintf("%s:4501", address)] = fdbv1beta2.None{}
@@ -126,7 +126,7 @@ var _ = Describe("bounceProcesses", func() {
 	Context("with a manually excluded process", func() {
 		BeforeEach(func() {
 			processGroup := cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-4]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 			for _, address := range processGroup.Addresses {
 				err := adminClient.ExcludeProcesses([]fdbv1beta2.ProcessAddress{{StringAddress: address, Port: 4501}})
@@ -141,7 +141,7 @@ var _ = Describe("bounceProcesses", func() {
 
 		It("should kill the targeted processes", func() {
 			addresses := make(map[string]fdbv1beta2.None, 1)
-			for _, processGroupID := range []string{"storage-1"} {
+			for _, processGroupID := range []fdbv1beta2.ProcessGroupID{"storage-1"} {
 				processGroupAddresses := fdbv1beta2.FindProcessGroupByID(cluster.Status.ProcessGroups, processGroupID).Addresses
 				for _, address := range processGroupAddresses {
 					addresses[fmt.Sprintf("%s:4501", address)] = fdbv1beta2.None{}
@@ -158,7 +158,7 @@ var _ = Describe("bounceProcesses", func() {
 	Context("with Pod in pending state", func() {
 		BeforeEach(func() {
 			processGroup := cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-4]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 			processGroup.UpdateCondition(fdbv1beta2.PodPending, true, nil, "")
 			cluster.Spec.AutomationOptions.IgnorePendingPodsDuration = 1 * time.Nanosecond
@@ -180,7 +180,7 @@ var _ = Describe("bounceProcesses", func() {
 	When("a process group has the MissingProcess condition", func() {
 		BeforeEach(func() {
 			processGroup := cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-4]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 			processGroup.ProcessGroupConditions = append(processGroup.ProcessGroupConditions, &fdbv1beta2.ProcessGroupCondition{
 				ProcessGroupConditionType: fdbv1beta2.MissingProcesses,
@@ -188,7 +188,7 @@ var _ = Describe("bounceProcesses", func() {
 			})
 
 			processGroup = cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-3]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-2"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 		})
 
@@ -217,11 +217,11 @@ var _ = Describe("bounceProcesses", func() {
 			Expect(err).NotTo(HaveOccurred())
 
 			processGroup := cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-4]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-5"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-5")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 
 			processGroup = cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-3]
-			Expect(processGroup.ProcessGroupID).To(Equal("storage-6"))
+			Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-6")))
 			processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 		})
 
@@ -231,7 +231,7 @@ var _ = Describe("bounceProcesses", func() {
 
 		It("should kill the targeted processes", func() {
 			addresses := make(map[string]fdbv1beta2.None, 2)
-			for _, processGroupID := range []string{"storage-5", "storage-6"} {
+			for _, processGroupID := range []fdbv1beta2.ProcessGroupID{"storage-5", "storage-6"} {
 				processGroupAddresses := fdbv1beta2.FindProcessGroupByID(cluster.Status.ProcessGroups, processGroupID).Addresses
 				for _, address := range processGroupAddresses {
 					addresses[fmt.Sprintf("%s:4501", address)] = fdbv1beta2.None{}
@@ -271,7 +271,7 @@ var _ = Describe("bounceProcesses", func() {
 		})
 
 		It("should submit pending upgrade information for all the processes", func() {
-			expectedUpgrades := make(map[string]bool, len(cluster.Status.ProcessGroups))
+			expectedUpgrades := make(map[fdbv1beta2.ProcessGroupID]bool, len(cluster.Status.ProcessGroups))
 			for _, processGroup := range cluster.Status.ProcessGroups {
 				expectedUpgrades[processGroup.ProcessGroupID] = true
 			}
@@ -305,7 +305,7 @@ var _ = Describe("bounceProcesses", func() {
 			})
 
 			It("should submit pending upgrade information for all the processes", func() {
-				expectedUpgrades := make(map[string]bool, len(cluster.Status.ProcessGroups))
+				expectedUpgrades := make(map[fdbv1beta2.ProcessGroupID]bool, len(cluster.Status.ProcessGroups))
 				for _, processGroup := range cluster.Status.ProcessGroups {
 					expectedUpgrades[processGroup.ProcessGroupID] = true
 				}
@@ -316,7 +316,7 @@ var _ = Describe("bounceProcesses", func() {
 
 			Context("with a pending upgrade for the unknown process", func() {
 				BeforeEach(func() {
-					err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.NextMajorVersion, []string{"dc2-storage-1"})
+					err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.NextMajorVersion, []fdbv1beta2.ProcessGroupID{"dc2-storage-1"})
 					Expect(err).NotTo(HaveOccurred())
 				})
 
@@ -338,7 +338,7 @@ var _ = Describe("bounceProcesses", func() {
 
 			Context("with a pending upgrade to an older version", func() {
 				BeforeEach(func() {
-					err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.NextPatchVersion, []string{"dc2-storage-1"})
+					err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.NextPatchVersion, []fdbv1beta2.ProcessGroupID{"dc2-storage-1"})
 					Expect(err).NotTo(HaveOccurred())
 				})
 
@@ -390,7 +390,7 @@ var _ = Describe("bounceProcesses", func() {
 
 			It("should requeue", func() {
 				Expect(requeue).NotTo(BeNil())
-				Expect(requeue.message).To(Equal(fmt.Sprintf("could not find address for processes: %s", []string{missingProcessGroup.ProcessGroupID})))
+				Expect(requeue.message).To(Equal(fmt.Sprintf("could not find address for processes: %s", []fdbv1beta2.ProcessGroupID{missingProcessGroup.ProcessGroupID})))
 			})
 
 			It("shouldn't kill any processes", func() {
@@ -469,7 +469,7 @@ var _ = Describe("bounceProcesses", func() {
 
 		BeforeEach(func() {
 			ignoredProcessGroup = cluster.Status.ProcessGroups[0]
-			cluster.Spec.Buggify.IgnoreDuringRestart = []string{ignoredProcessGroup.ProcessGroupID}
+			cluster.Spec.Buggify.IgnoreDuringRestart = []fdbv1beta2.ProcessGroupID{ignoredProcessGroup.ProcessGroupID}
 			for _, processGroup := range cluster.Status.ProcessGroups {
 				processGroup.UpdateCondition(fdbv1beta2.IncorrectCommandLine, true, nil, "")
 			}
@@ -524,7 +524,7 @@ var _ = Describe("bounceProcesses", func() {
 				Expect(err).NotTo(HaveOccurred())
 				processAddresses := make([]fdbv1beta2.ProcessAddress, 0, len(cluster.Status.ProcessGroups))
 				for _, process := range status.Cluster.Processes {
-					if process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey] != ignoredProcessGroup.ProcessGroupID {
+					if fdbv1beta2.ProcessGroupID(process.Locality[fdbv1beta2.FDBLocalityInstanceIDKey]) != ignoredProcessGroup.ProcessGroupID {
 						continue
 					}
 
diff --git a/controllers/change_coordinators_test.go b/controllers/change_coordinators_test.go
index 60add8aa..1d0d81ea 100644
--- a/controllers/change_coordinators_test.go
+++ b/controllers/change_coordinators_test.go
@@ -93,10 +93,10 @@ var _ = Describe("Change coordinators", func() {
 			})
 
 			When("when one storage process is marked for removal", func() {
-				removedProcess := "storage-2"
+				removedProcess := fdbv1beta2.ProcessGroupID("storage-2")
 
 				BeforeEach(func() {
-					cluster.Spec.ProcessGroupsToRemove = []string{
+					cluster.Spec.ProcessGroupsToRemove = []fdbv1beta2.ProcessGroupID{
 						removedProcess,
 					}
 					Expect(cluster.ProcessGroupIsBeingRemoved(removedProcess)).To(BeTrue())
@@ -133,7 +133,7 @@ var _ = Describe("Change coordinators", func() {
 			})
 
 			When("when multiple storage process are marked for removal", func() {
-				removals := []string{
+				removals := []fdbv1beta2.ProcessGroupID{
 					"storage-2",
 					"storage-3",
 				}
@@ -209,7 +209,7 @@ var _ = Describe("Change coordinators", func() {
 			var status *fdbv1beta2.FoundationDBStatus
 			var candidates []locality.Info
 			var excludes []string
-			var removals []string
+			var removals []fdbv1beta2.ProcessGroupID
 			var dcCnt int
 			var satCnt int
 			var shouldFail bool
@@ -218,7 +218,7 @@ var _ = Describe("Change coordinators", func() {
 				// ensure a clean state
 				candidates = []locality.Info{}
 				excludes = []string{}
-				removals = []string{}
+				removals = []fdbv1beta2.ProcessGroupID{}
 				shouldFail = false
 			})
 
@@ -331,7 +331,7 @@ var _ = Describe("Change coordinators", func() {
 
 				When("some processes are removed", func() {
 					BeforeEach(func() {
-						removals = []string{
+						removals = []fdbv1beta2.ProcessGroupID{
 							"dc0-storage-1",
 							"dc0-storage-2",
 							"dc0-storage-3",
@@ -501,7 +501,7 @@ var _ = Describe("Change coordinators", func() {
 
 				When("some processes are removed", func() {
 					BeforeEach(func() {
-						removals = []string{
+						removals = []fdbv1beta2.ProcessGroupID{
 							"dc0-storage-1",
 							"dc0-storage-2",
 							"dc0-storage-3",
@@ -682,8 +682,8 @@ var _ = Describe("Change coordinators", func() {
 	})
 })
 
-func generateProcessInfo(dcCount int, satCount int, excludes []string) map[string]fdbv1beta2.FoundationDBStatusProcessInfo {
-	res := map[string]fdbv1beta2.FoundationDBStatusProcessInfo{}
+func generateProcessInfo(dcCount int, satCount int, excludes []string) map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo {
+	res := map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{}
 	logCnt := 4
 
 	for i := 0; i < dcCount; i++ {
@@ -702,7 +702,7 @@ func generateProcessInfo(dcCount int, satCount int, excludes []string) map[strin
 	return res
 }
 
-func generateProcessInfoDetails(res map[string]fdbv1beta2.FoundationDBStatusProcessInfo, dcID string, cnt int, excludes []string, pClass fdbv1beta2.ProcessClass) {
+func generateProcessInfoDetails(res map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo, dcID string, cnt int, excludes []string, pClass fdbv1beta2.ProcessClass) {
 	for idx := 0; idx < cnt; idx++ {
 		excluded := false
 		zoneID := fmt.Sprintf("%s-%s-%d", dcID, pClass, idx)
@@ -717,7 +717,7 @@ func generateProcessInfoDetails(res map[string]fdbv1beta2.FoundationDBStatusProc
 		}
 
 		addr := fmt.Sprintf("1.1.1.%d:4501", len(res))
-		res[zoneID] = fdbv1beta2.FoundationDBStatusProcessInfo{
+		res[fdbv1beta2.ProcessGroupID(zoneID)] = fdbv1beta2.FoundationDBStatusProcessInfo{
 			ProcessClass: pClass,
 			Locality: map[string]string{
 				fdbv1beta2.FDBLocalityInstanceIDKey: zoneID,
diff --git a/controllers/choose_removals_test.go b/controllers/choose_removals_test.go
index 6b827fdf..de2310c9 100644
--- a/controllers/choose_removals_test.go
+++ b/controllers/choose_removals_test.go
@@ -37,7 +37,7 @@ var _ = Describe("choose_removals", func() {
 	var adminClient *mock.AdminClient
 	var err error
 	var requeue *requeue
-	var removals []string
+	var removals []fdbv1beta2.ProcessGroupID
 
 	BeforeEach(func() {
 		cluster = internal.CreateDefaultCluster()
@@ -91,13 +91,13 @@ var _ = Describe("choose_removals", func() {
 		})
 
 		It("should mark one of the process groups for removal", func() {
-			Expect(removals).To(Equal([]string{"storage-4"}))
+			Expect(removals).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-4"}))
 		})
 
 		Context("with a process group already marked", func() {
 			BeforeEach(func() {
 				processGroup := cluster.Status.ProcessGroups[len(cluster.Status.ProcessGroups)-3]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-2"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 				processGroup.MarkForRemoval()
 				err = clusterReconciler.updateOrApply(context.TODO(), cluster)
 				Expect(err).NotTo(HaveOccurred())
@@ -108,7 +108,7 @@ var _ = Describe("choose_removals", func() {
 			})
 
 			It("should leave that process group for removal", func() {
-				Expect(removals).To(Equal([]string{"storage-2"}))
+				Expect(removals).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-2"}))
 			})
 		})
 
@@ -123,7 +123,7 @@ var _ = Describe("choose_removals", func() {
 			})
 
 			It("should mark one of the process groups on that rack for removal", func() {
-				Expect(removals).To(Equal([]string{"storage-2"}))
+				Expect(removals).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-2"}))
 			})
 		})
 	})
@@ -139,7 +139,7 @@ var _ = Describe("choose_removals", func() {
 		})
 
 		It("should mark two of the process groups for removal", func() {
-			Expect(removals).To(Equal([]string{"cluster_controller-1", "storage-4"}))
+			Expect(removals).To(Equal([]fdbv1beta2.ProcessGroupID{"cluster_controller-1", "storage-4"}))
 		})
 	})
 
diff --git a/controllers/cluster_controller_test.go b/controllers/cluster_controller_test.go
index 779e624e..f680be2a 100644
--- a/controllers/cluster_controller_test.go
+++ b/controllers/cluster_controller_test.go
@@ -339,7 +339,7 @@ var _ = Describe("cluster_controller", func() {
 		})
 		Context("when buggifying a pod to make it crash loop", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"storage-1"}
+				cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-1"}
 				err = k8sClient.Update(context.TODO(), cluster)
 				Expect(err).NotTo(HaveOccurred())
 			})
@@ -560,8 +560,8 @@ var _ = Describe("cluster_controller", func() {
 
 			Context("with an entry in the process groups to remove list", func() {
 				BeforeEach(func() {
-					cluster.Spec.ProcessGroupsToRemove = []string{
-						originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+					cluster.Spec.ProcessGroupsToRemove = []fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 					}
 					err := k8sClient.Update(context.TODO(), cluster)
 					Expect(err).NotTo(HaveOccurred())
@@ -611,8 +611,8 @@ var _ = Describe("cluster_controller", func() {
 				})
 
 				It("should clear the removal list", func() {
-					Expect(cluster.Spec.ProcessGroupsToRemove).To(Equal([]string{
-						originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+					Expect(cluster.Spec.ProcessGroupsToRemove).To(Equal([]fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 					}))
 				})
 
@@ -622,7 +622,7 @@ var _ = Describe("cluster_controller", func() {
 
 					Expect(fdbv1beta2.ContainsProcessGroupID(processGroups, "storage-5")).To(BeTrue())
 					oldID := originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]
-					Expect(fdbv1beta2.ContainsProcessGroupID(processGroups, oldID)).To(BeFalse())
+					Expect(fdbv1beta2.ContainsProcessGroupID(processGroups, fdbv1beta2.ProcessGroupID(oldID))).To(BeFalse())
 
 					for _, group := range processGroups {
 						Expect(group.IsMarkedForRemoval()).To(BeFalse())
@@ -633,7 +633,7 @@ var _ = Describe("cluster_controller", func() {
 					BeforeEach(func() {
 						pod := originalPods.Items[firstStorageIndex]
 						for _, processGroup := range cluster.Status.ProcessGroups {
-							if processGroup.ProcessGroupID == pod.ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel] {
+							if processGroup.ProcessGroupID == fdbv1beta2.ProcessGroupID(pod.ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]) {
 								processGroup.UpdateCondition(fdbv1beta2.MissingProcesses, true, nil, "")
 							}
 						}
@@ -673,7 +673,7 @@ var _ = Describe("cluster_controller", func() {
 					BeforeEach(func() {
 						originalPod := &originalPods.Items[firstStorageIndex]
 						for _, processGroup := range cluster.Status.ProcessGroups {
-							if processGroup.ProcessGroupID == originalPod.ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel] {
+							if processGroup.ProcessGroupID == fdbv1beta2.ProcessGroupID(originalPod.ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]) {
 								processGroup.UpdateCondition(fdbv1beta2.MissingProcesses, true, nil, "")
 							}
 						}
@@ -726,8 +726,8 @@ var _ = Describe("cluster_controller", func() {
 
 				Context("with an entry in the process groups to remove list", func() {
 					BeforeEach(func() {
-						cluster.Spec.ProcessGroupsToRemove = []string{
-							originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+						cluster.Spec.ProcessGroupsToRemove = []fdbv1beta2.ProcessGroupID{
+							fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 						}
 						err := k8sClient.Update(context.TODO(), cluster)
 						Expect(err).NotTo(HaveOccurred())
@@ -757,8 +757,8 @@ var _ = Describe("cluster_controller", func() {
 					err := k8sClient.RemovePodIP(&originalPods.Items[firstStorageIndex])
 					Expect(err).NotTo(HaveOccurred())
 
-					cluster.Spec.ProcessGroupsToRemove = []string{
-						originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+					cluster.Spec.ProcessGroupsToRemove = []fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 					}
 					err = k8sClient.Update(context.TODO(), cluster)
 					Expect(err).NotTo(HaveOccurred())
@@ -788,8 +788,8 @@ var _ = Describe("cluster_controller", func() {
 				})
 
 				It("should clear the removal list", func() {
-					Expect(cluster.Spec.ProcessGroupsToRemove).To(Equal([]string{
-						originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+					Expect(cluster.Spec.ProcessGroupsToRemove).To(Equal([]fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 					}))
 				})
 			})
@@ -798,8 +798,8 @@ var _ = Describe("cluster_controller", func() {
 				BeforeEach(func() {
 					err := k8sClient.RemovePodIP(&originalPods.Items[firstStorageIndex])
 					Expect(err).NotTo(HaveOccurred())
-					cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = []string{
-						originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+					cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = []fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 					}
 					err = k8sClient.Update(context.TODO(), cluster)
 					Expect(err).NotTo(HaveOccurred())
@@ -828,8 +828,8 @@ var _ = Describe("cluster_controller", func() {
 				})
 
 				It("should clear the removal list", func() {
-					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(Equal([]string{
-						originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(Equal([]fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 					}))
 				})
 			})
@@ -892,8 +892,8 @@ var _ = Describe("cluster_controller", func() {
 				adminClient, err = mock.NewMockAdminClientUncast(cluster, k8sClient)
 				Expect(err).NotTo(HaveOccurred())
 
-				cluster.Spec.ProcessGroupsToRemove = []string{
-					originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+				cluster.Spec.ProcessGroupsToRemove = []fdbv1beta2.ProcessGroupID{
+					fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 				}
 				err := k8sClient.Update(context.TODO(), cluster)
 				Expect(err).NotTo(HaveOccurred())
@@ -936,8 +936,8 @@ var _ = Describe("cluster_controller", func() {
 
 		Context("with multiple replacements", func() {
 			BeforeEach(func() {
-				cluster.Spec.ProcessGroupsToRemove = []string{
-					originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel],
+				cluster.Spec.ProcessGroupsToRemove = []fdbv1beta2.ProcessGroupID{
+					fdbv1beta2.ProcessGroupID(originalPods.Items[firstStorageIndex].ObjectMeta.Labels[fdbv1beta2.FDBProcessGroupIDLabel]),
 					"storage-5",
 				}
 				err := k8sClient.Update(context.TODO(), cluster)
@@ -1286,7 +1286,7 @@ var _ = Describe("cluster_controller", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				for _, item := range pods.Items {
-					_, id, err := podmanager.ParseProcessGroupID(item.Labels[fdbv1beta2.FDBProcessGroupIDLabel])
+					_, id, err := podmanager.ParseProcessGroupID(fdbv1beta2.ProcessGroupID(item.Labels[fdbv1beta2.FDBProcessGroupIDLabel]))
 					Expect(err).NotTo(HaveOccurred())
 
 					hash, err := internal.GetPodSpecHash(cluster, internal.ProcessClassFromLabels(cluster, item.Labels), id, nil)
@@ -1425,7 +1425,7 @@ var _ = Describe("cluster_controller", func() {
 					err = k8sClient.List(context.TODO(), pods, getListOptions(cluster)...)
 					Expect(err).NotTo(HaveOccurred())
 					for _, item := range pods.Items {
-						_, id, err := podmanager.ParseProcessGroupID(item.Labels[fdbv1beta2.FDBProcessGroupIDLabel])
+						_, id, err := podmanager.ParseProcessGroupID(fdbv1beta2.ProcessGroupID(item.Labels[fdbv1beta2.FDBProcessGroupIDLabel]))
 						Expect(err).NotTo(HaveOccurred())
 
 						hash, err := internal.GetPodSpecHash(cluster, internal.ProcessClassFromLabels(cluster, item.Labels), id, nil)
@@ -1533,7 +1533,7 @@ var _ = Describe("cluster_controller", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				for _, item := range pods.Items {
-					_, id, err := podmanager.ParseProcessGroupID(item.Labels[fdbv1beta2.FDBProcessGroupIDLabel])
+					_, id, err := podmanager.ParseProcessGroupID(fdbv1beta2.ProcessGroupID(item.Labels[fdbv1beta2.FDBProcessGroupIDLabel]))
 					Expect(err).NotTo(HaveOccurred())
 
 					hash, err := internal.GetPodSpecHash(cluster, internal.ProcessClassFromLabels(cluster, item.Labels), id, nil)
@@ -2687,7 +2687,7 @@ var _ = Describe("cluster_controller", func() {
 				_, err = reloadClusterGenerations(cluster)
 				Expect(err).NotTo(HaveOccurred())
 				incorrectProcesses := fdbv1beta2.FilterByCondition(cluster.Status.ProcessGroups, fdbv1beta2.IncorrectCommandLine, false)
-				Expect(incorrectProcesses).To(Equal([]string{"storage-1"}))
+				Expect(incorrectProcesses).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1"}))
 			})
 
 			When("When an additional process have an incorrect commandline", func() {
@@ -2702,7 +2702,7 @@ var _ = Describe("cluster_controller", func() {
 					_, err = reloadClusterGenerations(cluster)
 					Expect(err).NotTo(HaveOccurred())
 					incorrectProcesses := fdbv1beta2.FilterByCondition(cluster.Status.ProcessGroups, fdbv1beta2.IncorrectCommandLine, false)
-					Expect(incorrectProcesses).To(Equal([]string{"storage-1", "storage-2"}))
+					Expect(incorrectProcesses).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"}))
 				})
 			})
 		})
diff --git a/controllers/delete_pods_for_buggification_test.go b/controllers/delete_pods_for_buggification_test.go
index e5182c90..f298b54e 100644
--- a/controllers/delete_pods_for_buggification_test.go
+++ b/controllers/delete_pods_for_buggification_test.go
@@ -92,7 +92,7 @@ var _ = Describe("delete_pods_for_buggification", func() {
 	Context("with a buggification that needs to be enabled", func() {
 		When("using crashLoop", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"storage-1"}
+				cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-1"}
 			})
 
 			It("should requeue", func() {
@@ -115,7 +115,7 @@ var _ = Describe("delete_pods_for_buggification", func() {
 
 		When("using noSchedule", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.NoSchedule = []string{"storage-1"}
+				cluster.Spec.Buggify.NoSchedule = []fdbv1beta2.ProcessGroupID{"storage-1"}
 			})
 
 			It("should requeue", func() {
@@ -139,7 +139,7 @@ var _ = Describe("delete_pods_for_buggification", func() {
 
 	Context("with a wildcard buggification", func() {
 		BeforeEach(func() {
-			cluster.Spec.Buggify.CrashLoop = []string{"*"}
+			cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"*"}
 		})
 
 		It("should requeue", func() {
@@ -257,7 +257,7 @@ var _ = Describe("delete_pods_for_buggification", func() {
 				pod.Spec.Containers[0].Args = []string{"crash-loop"}
 				err = k8sClient.Create(context.TODO(), pod)
 				Expect(err).NotTo(HaveOccurred())
-				cluster.Spec.Buggify.CrashLoop = []string{"storage-1"}
+				cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-1"}
 			})
 
 			It("should not requeue", func() {
@@ -304,7 +304,7 @@ var _ = Describe("delete_pods_for_buggification", func() {
 				pod.ResourceVersion = ""
 				err = k8sClient.Create(context.TODO(), pod)
 				Expect(err).NotTo(HaveOccurred())
-				cluster.Spec.Buggify.NoSchedule = []string{"storage-1"}
+				cluster.Spec.Buggify.NoSchedule = []fdbv1beta2.ProcessGroupID{"storage-1"}
 			})
 
 			It("should not requeue", func() {
diff --git a/controllers/exclude_processes_test.go b/controllers/exclude_processes_test.go
index 2e384ca3..50ce3e0d 100644
--- a/controllers/exclude_processes_test.go
+++ b/controllers/exclude_processes_test.go
@@ -82,7 +82,7 @@ var _ = Describe("exclude_processes", func() {
 				It("should not allow the exclusion", func() {
 					canExclude, missing := canExcludeNewProcesses(cluster, fdbv1beta2.ProcessClassStorage)
 					Expect(canExclude).To(BeFalse())
-					Expect(missing).To(Equal([]string{"storage-1", "storage-2"}))
+					Expect(missing).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"}))
 				})
 			})
 
@@ -133,7 +133,7 @@ var _ = Describe("exclude_processes", func() {
 				It("should not allow the exclusion", func() {
 					canExclude, missing := canExcludeNewProcesses(cluster, fdbv1beta2.ProcessClassStorage)
 					Expect(canExclude).To(BeFalse())
-					Expect(missing).To(Equal([]string{"storage-1", "storage-10", "storage-11", "storage-12", "storage-13"}))
+					Expect(missing).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1", "storage-10", "storage-11", "storage-12", "storage-13"}))
 				})
 			})
 		})
@@ -189,7 +189,7 @@ var _ = Describe("exclude_processes", func() {
 			When("excluding one process", func() {
 				BeforeEach(func() {
 					processGroup := cluster.Status.ProcessGroups[0]
-					Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+					Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					processGroup.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = processGroup
 				})
@@ -206,12 +206,12 @@ var _ = Describe("exclude_processes", func() {
 			When("excluding two process", func() {
 				BeforeEach(func() {
 					processGroup1 := cluster.Status.ProcessGroups[0]
-					Expect(processGroup1.ProcessGroupID).To(Equal("storage-1"))
+					Expect(processGroup1.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					processGroup1.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = processGroup1
 
 					processGroup2 := cluster.Status.ProcessGroups[1]
-					Expect(processGroup2.ProcessGroupID).To(Equal("storage-2"))
+					Expect(processGroup2.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 					processGroup2.MarkForRemoval()
 					cluster.Status.ProcessGroups[1] = processGroup2
 				})
@@ -228,12 +228,12 @@ var _ = Describe("exclude_processes", func() {
 			When("excluding two process with one already excluded", func() {
 				BeforeEach(func() {
 					processGroup1 := cluster.Status.ProcessGroups[0]
-					Expect(processGroup1.ProcessGroupID).To(Equal("storage-1"))
+					Expect(processGroup1.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					processGroup1.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = processGroup1
 
 					processGroup2 := cluster.Status.ProcessGroups[1]
-					Expect(processGroup2.ProcessGroupID).To(Equal("storage-2"))
+					Expect(processGroup2.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 					processGroup2.MarkForRemoval()
 					cluster.Status.ProcessGroups[1] = processGroup2
 
@@ -266,7 +266,7 @@ var _ = Describe("exclude_processes", func() {
 			When("excluding one process", func() {
 				BeforeEach(func() {
 					processGroup := cluster.Status.ProcessGroups[0]
-					Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+					Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					processGroup.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = processGroup
 				})
@@ -283,12 +283,12 @@ var _ = Describe("exclude_processes", func() {
 			When("excluding two process", func() {
 				BeforeEach(func() {
 					processGroup1 := cluster.Status.ProcessGroups[0]
-					Expect(processGroup1.ProcessGroupID).To(Equal("storage-1"))
+					Expect(processGroup1.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					processGroup1.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = processGroup1
 
 					processGroup2 := cluster.Status.ProcessGroups[1]
-					Expect(processGroup2.ProcessGroupID).To(Equal("storage-2"))
+					Expect(processGroup2.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 					processGroup2.MarkForRemoval()
 					cluster.Status.ProcessGroups[1] = processGroup2
 				})
@@ -305,12 +305,12 @@ var _ = Describe("exclude_processes", func() {
 			When("excluding two process with one already excluded using IP", func() {
 				BeforeEach(func() {
 					processGroup1 := cluster.Status.ProcessGroups[0]
-					Expect(processGroup1.ProcessGroupID).To(Equal("storage-1"))
+					Expect(processGroup1.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					processGroup1.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = processGroup1
 
 					processGroup2 := cluster.Status.ProcessGroups[1]
-					Expect(processGroup2.ProcessGroupID).To(Equal("storage-2"))
+					Expect(processGroup2.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 					processGroup2.MarkForRemoval()
 					cluster.Status.ProcessGroups[1] = processGroup2
 
@@ -329,12 +329,12 @@ var _ = Describe("exclude_processes", func() {
 			When("excluding two process with one already excluded using locality", func() {
 				BeforeEach(func() {
 					processGroup1 := cluster.Status.ProcessGroups[0]
-					Expect(processGroup1.ProcessGroupID).To(Equal("storage-1"))
+					Expect(processGroup1.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					processGroup1.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = processGroup1
 
 					processGroup2 := cluster.Status.ProcessGroups[1]
-					Expect(processGroup2.ProcessGroupID).To(Equal("storage-2"))
+					Expect(processGroup2.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-2")))
 					processGroup2.MarkForRemoval()
 					cluster.Status.ProcessGroups[1] = processGroup2
 
diff --git a/controllers/remove_incompatible_processes_test.go b/controllers/remove_incompatible_processes_test.go
index ae199f78..f7773cc3 100644
--- a/controllers/remove_incompatible_processes_test.go
+++ b/controllers/remove_incompatible_processes_test.go
@@ -83,7 +83,7 @@ var _ = Describe("restart_incompatible_pods", func() {
 					IncompatibleConnections: []string{
 						"1.1.1.1:0:tls",
 					},
-					Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+					Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 						"2": {
 							Address: fdbv1beta2.ProcessAddress{
 								IPAddress: net.ParseIP("1.1.1.2"),
@@ -105,7 +105,7 @@ var _ = Describe("restart_incompatible_pods", func() {
 						"1.1.1.1:0:tls",
 						"1.1.1.2:0:tls",
 					},
-					Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+					Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 						"2": {
 							Address: fdbv1beta2.ProcessAddress{
 								IPAddress: net.ParseIP("1.1.1.2"),
@@ -235,7 +235,7 @@ var _ = Describe("restart_incompatible_pods", func() {
 					BeforeEach(func() {
 						adminClient, err := mock.NewMockAdminClientUncast(cluster, k8sClient)
 						Expect(err).NotTo(HaveOccurred())
-						adminClient.FrozenStatus.Cluster.Processes = map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+						adminClient.FrozenStatus.Cluster.Processes = map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 							"1": {
 								Address: fdbv1beta2.ProcessAddress{
 									IPAddress: net.ParseIP(cluster.Status.ProcessGroups[0].Addresses[0]),
diff --git a/controllers/remove_process_groups_test.go b/controllers/remove_process_groups_test.go
index 507c0963..705955d0 100644
--- a/controllers/remove_process_groups_test.go
+++ b/controllers/remove_process_groups_test.go
@@ -62,7 +62,7 @@ var _ = Describe("remove_process_groups", func() {
 
 		When("trying to remove a coordinator", func() {
 			coordinatorIP := "1.1.1.1"
-			coordinatorID := "storage-1"
+			coordinatorID := fdbv1beta2.ProcessGroupID("storage-1")
 
 			BeforeEach(func() {
 				marked, processGroup := fdbv1beta2.MarkProcessGroupForRemoval(cluster.Status.ProcessGroups, coordinatorID, fdbv1beta2.ProcessClassStorage, coordinatorIP)
@@ -295,7 +295,7 @@ var _ = Describe("remove_process_groups", func() {
 	})
 
 	Context("validating getProcessesToInclude", func() {
-		var removedProcessGroups map[string]bool
+		var removedProcessGroups map[fdbv1beta2.ProcessGroupID]bool
 
 		BeforeEach(func() {
 			cluster = &fdbv1beta2.FoundationDBCluster{
@@ -325,7 +325,7 @@ var _ = Describe("remove_process_groups", func() {
 					},
 				},
 			}
-			removedProcessGroups = make(map[string]bool)
+			removedProcessGroups = make(map[fdbv1beta2.ProcessGroupID]bool)
 		})
 
 		Context("cluster doesn't support inclusions using locality", func() {
@@ -342,7 +342,7 @@ var _ = Describe("remove_process_groups", func() {
 			When("including one process", func() {
 				BeforeEach(func() {
 					processGroup := cluster.Status.ProcessGroups[0]
-					Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+					Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					processGroup.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = processGroup
 
@@ -376,7 +376,7 @@ var _ = Describe("remove_process_groups", func() {
 
 				BeforeEach(func() {
 					removedProcessGroup = cluster.Status.ProcessGroups[0]
-					Expect(removedProcessGroup.ProcessGroupID).To(Equal("storage-1"))
+					Expect(removedProcessGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 					removedProcessGroup.MarkForRemoval()
 					cluster.Status.ProcessGroups[0] = removedProcessGroup
 
diff --git a/controllers/replace_failed_process_groups_test.go b/controllers/replace_failed_process_groups_test.go
index 563fc8ee..f7cbc92d 100644
--- a/controllers/replace_failed_process_groups_test.go
+++ b/controllers/replace_failed_process_groups_test.go
@@ -70,7 +70,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 			})
 
 		It("should not mark anything for removal", func() {
-			Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+			Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 		})
 	})
 
@@ -90,7 +90,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 			})
 
 			It("should mark the process group for removal", func() {
-				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{"storage-2"}))
+				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-2"}))
 			})
 
 			It("should not be marked to skip exclusion", func() {
@@ -113,13 +113,13 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should not mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 				})
 			})
 
 			When("Crash loop is set for all process groups", func() {
 				BeforeEach(func() {
-					cluster.Spec.Buggify.CrashLoop = []string{"*"}
+					cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"*"}
 				})
 
 				It("should return nil", func() {
@@ -127,13 +127,13 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should not mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 				})
 			})
 
 			When("Crash loop is set for the specific process group", func() {
 				BeforeEach(func() {
-					cluster.Spec.Buggify.CrashLoop = []string{"storage-2"}
+					cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-2"}
 				})
 
 				It("should return nil", func() {
@@ -141,7 +141,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should not mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 				})
 			})
 
@@ -150,7 +150,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 					cluster.Spec.Buggify.CrashLoopContainers = []fdbv1beta2.CrashLoopContainerObject{
 						{
 							ContainerName: fdbv1beta2.MainContainerName,
-							Targets:       []string{"storage-2"},
+							Targets:       []fdbv1beta2.ProcessGroupID{"storage-2"},
 						},
 					}
 				})
@@ -160,7 +160,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should not mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 				})
 			})
 
@@ -169,7 +169,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 					cluster.Spec.Buggify.CrashLoopContainers = []fdbv1beta2.CrashLoopContainerObject{
 						{
 							ContainerName: fdbv1beta2.SidecarContainerName,
-							Targets:       []string{"storage-2"},
+							Targets:       []fdbv1beta2.ProcessGroupID{"storage-2"},
 						},
 					}
 				})
@@ -179,7 +179,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should not mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 				})
 			})
 		})
@@ -199,7 +199,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 			})
 
 			It("should mark the first process group for removal", func() {
-				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{"storage-2"}))
+				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-2"}))
 			})
 
 			It("should not be marked to skip exclusion", func() {
@@ -224,7 +224,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 			})
 
 			It("should not mark the process group for removal", func() {
-				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{"storage-3"}))
+				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-3"}))
 			})
 
 			When("max concurrent replacements is set to two", func() {
@@ -238,7 +238,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{"storage-2", "storage-3"}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-2", "storage-3"}))
 				})
 			})
 
@@ -252,7 +252,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should not mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{"storage-3"}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-3"}))
 				})
 			})
 		})
@@ -270,7 +270,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 			})
 
 			It("should mark the process group for removal", func() {
-				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{"storage-2", "storage-3"}))
+				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-2", "storage-3"}))
 			})
 		})
 
@@ -286,7 +286,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 			})
 
 			It("should mark the process group for removal", func() {
-				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{"storage-2"}))
+				Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-2"}))
 			})
 
 			It("should marked to skip exclusion", func() {
@@ -320,7 +320,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should not mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 				})
 			})
 
@@ -339,7 +339,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 				})
 
 				It("should not mark the process group for removal", func() {
-					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+					Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 				})
 			})
 		})
@@ -359,7 +359,7 @@ var _ = Describe("replace_failed_process_groups", func() {
 		})
 
 		It("should not mark the process group for removal", func() {
-			Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+			Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 		})
 	})
 
@@ -377,15 +377,15 @@ var _ = Describe("replace_failed_process_groups", func() {
 		})
 
 		It("should not mark the process group for removal", func() {
-			Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]string{}))
+			Expect(getRemovedProcessGroupIDs(cluster)).To(Equal([]fdbv1beta2.ProcessGroupID{}))
 		})
 	})
 })
 
 // getRemovedProcessGroupIDs returns a list of ids for the process groups that
 // are marked for removal.
-func getRemovedProcessGroupIDs(cluster *fdbv1beta2.FoundationDBCluster) []string {
-	results := make([]string, 0)
+func getRemovedProcessGroupIDs(cluster *fdbv1beta2.FoundationDBCluster) []fdbv1beta2.ProcessGroupID {
+	results := make([]fdbv1beta2.ProcessGroupID, 0)
 	for _, processGroupStatus := range cluster.Status.ProcessGroups {
 		if processGroupStatus.IsMarkedForRemoval() {
 			results = append(results, processGroupStatus.ProcessGroupID)
diff --git a/controllers/update_pods_test.go b/controllers/update_pods_test.go
index dfdd4602..0b7f2d94 100644
--- a/controllers/update_pods_test.go
+++ b/controllers/update_pods_test.go
@@ -110,7 +110,7 @@ var _ = Describe("update_pods", func() {
 	})
 
 	Context("Validating shouldRequeueDueToTerminatingPod", func() {
-		var processGroup = ""
+		var processGroup = fdbv1beta2.ProcessGroupID("")
 
 		When("pod is without deletionTimestamp", func() {
 			var cluster *fdbv1beta2.FoundationDBCluster
diff --git a/controllers/update_status_test.go b/controllers/update_status_test.go
index efbc7c36..8732ed9f 100644
--- a/controllers/update_status_test.go
+++ b/controllers/update_status_test.go
@@ -46,7 +46,7 @@ var _ = Describe("update_status", func() {
 		var configMap *corev1.ConfigMap
 		var adminClient *mock.AdminClient
 		var pods []*corev1.Pod
-		var processMap map[string][]fdbv1beta2.FoundationDBStatusProcessInfo
+		var processMap map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.FoundationDBStatusProcessInfo
 		var err error
 		var allPods []*corev1.Pod
 		var allPvcs *corev1.PersistentVolumeClaimList
@@ -74,14 +74,14 @@ var _ = Describe("update_status", func() {
 		JustBeforeEach(func() {
 			databaseStatus, err := adminClient.GetStatus()
 			Expect(err).NotTo(HaveOccurred())
-			processMap = make(map[string][]fdbv1beta2.FoundationDBStatusProcessInfo)
+			processMap = make(map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.FoundationDBStatusProcessInfo)
 			for _, process := range databaseStatus.Cluster.Processes {
 				processID, ok := process.Locality["process_id"]
 				// if the processID is not set we fall back to the instanceID
 				if !ok {
 					processID = process.Locality["instance_id"]
 				}
-				processMap[processID] = append(processMap[processID], process)
+				processMap[fdbv1beta2.ProcessGroupID(processID)] = append(processMap[fdbv1beta2.ProcessGroupID(processID)], process)
 			}
 
 			allPods, err = clusterReconciler.PodLifecycleManager.GetPods(context.TODO(), clusterReconciler, cluster, internal.GetPodListOptions(cluster, "", "")...)
@@ -110,7 +110,7 @@ var _ = Describe("update_status", func() {
 				Expect(err).NotTo(HaveOccurred())
 				Expect(len(processGroupStatus)).To(BeNumerically(">", 4))
 				processGroup := processGroupStatus[len(processGroupStatus)-4]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 				Expect(len(processGroupStatus[0].ProcessGroupConditions)).To(Equal(0))
 			})
 		})
@@ -125,11 +125,11 @@ var _ = Describe("update_status", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				missingProcesses := fdbv1beta2.FilterByCondition(processGroupStatus, fdbv1beta2.MissingPod, false)
-				Expect(missingProcesses).To(Equal([]string{"storage-1"}))
+				Expect(missingProcesses).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1"}))
 
 				Expect(len(processGroupStatus)).To(BeNumerically(">", 4))
 				processGroup := processGroupStatus[len(processGroupStatus)-4]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 				Expect(len(processGroup.ProcessGroupConditions)).To(Equal(1))
 			})
 		})
@@ -144,11 +144,11 @@ var _ = Describe("update_status", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				incorrectProcesses := fdbv1beta2.FilterByCondition(processGroupStatus, fdbv1beta2.IncorrectCommandLine, false)
-				Expect(incorrectProcesses).To(Equal([]string{"storage-1"}))
+				Expect(incorrectProcesses).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1"}))
 
 				Expect(len(processGroupStatus)).To(BeNumerically(">", 4))
 				processGroup := processGroupStatus[len(processGroupStatus)-4]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 				Expect(len(processGroup.ProcessGroupConditions)).To(Equal(1))
 			})
 		})
@@ -163,11 +163,11 @@ var _ = Describe("update_status", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				missingProcesses := fdbv1beta2.FilterByCondition(processGroupStatus, fdbv1beta2.MissingProcesses, false)
-				Expect(missingProcesses).To(Equal([]string{"storage-1"}))
+				Expect(missingProcesses).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1"}))
 
 				Expect(len(processGroupStatus)).To(BeNumerically(">", 4))
 				processGroup := processGroupStatus[len(processGroupStatus)-4]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 				Expect(len(processGroup.ProcessGroupConditions)).To(Equal(1))
 			})
 		})
@@ -184,11 +184,11 @@ var _ = Describe("update_status", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				incorrectPods := fdbv1beta2.FilterByCondition(processGroupStatus, fdbv1beta2.IncorrectPodSpec, false)
-				Expect(incorrectPods).To(Equal([]string{"storage-1"}))
+				Expect(incorrectPods).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1"}))
 
 				Expect(len(processGroupStatus)).To(BeNumerically(">", 4))
 				processGroup := processGroupStatus[len(processGroupStatus)-4]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 				Expect(len(processGroup.ProcessGroupConditions)).To(Equal(1))
 			})
 		})
@@ -210,11 +210,11 @@ var _ = Describe("update_status", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				missingProcesses := fdbv1beta2.FilterByCondition(processGroupStatus, fdbv1beta2.PodFailing, false)
-				Expect(missingProcesses).To(Equal([]string{"storage-1"}))
+				Expect(missingProcesses).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1"}))
 
 				Expect(len(processGroupStatus)).To(BeNumerically(">", 4))
 				processGroup := processGroupStatus[len(processGroupStatus)-4]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 				Expect(len(processGroup.ProcessGroupConditions)).To(Equal(1))
 			})
 		})
@@ -231,11 +231,11 @@ var _ = Describe("update_status", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				failingPods := fdbv1beta2.FilterByCondition(processGroupStatus, fdbv1beta2.PodFailing, false)
-				Expect(failingPods).To(Equal([]string{"storage-1"}))
+				Expect(failingPods).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1"}))
 
 				Expect(len(processGroupStatus)).To(BeNumerically(">", 4))
 				processGroup := processGroupStatus[len(processGroupStatus)-4]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 				Expect(len(processGroup.ProcessGroupConditions)).To(Equal(1))
 			})
 		})
@@ -252,24 +252,24 @@ var _ = Describe("update_status", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				failingPods := fdbv1beta2.FilterByCondition(processGroupStatus, fdbv1beta2.PodFailing, false)
-				Expect(failingPods).To(Equal([]string{"storage-1"}))
+				Expect(failingPods).To(Equal([]fdbv1beta2.ProcessGroupID{"storage-1"}))
 
 				Expect(len(processGroupStatus)).To(BeNumerically(">", 4))
 				processGroup := processGroupStatus[len(processGroupStatus)-4]
-				Expect(processGroup.ProcessGroupID).To(Equal("storage-1"))
+				Expect(processGroup.ProcessGroupID).To(Equal(fdbv1beta2.ProcessGroupID("storage-1")))
 				Expect(len(processGroup.ProcessGroupConditions)).To(Equal(1))
 			})
 		})
 
 		When("adding a process group to the ProcessGroupsToRemove list", func() {
-			var removedProcessGroup string
+			var removedProcessGroup fdbv1beta2.ProcessGroupID
 
 			BeforeEach(func() {
 				removedProcessGroup = "storage-1"
 				pods[0].Status.Phase = corev1.PodFailed
 				err = k8sClient.Update(context.TODO(), pods[0])
 				Expect(err).NotTo(HaveOccurred())
-				cluster.Spec.ProcessGroupsToRemove = []string{removedProcessGroup}
+				cluster.Spec.ProcessGroupsToRemove = []fdbv1beta2.ProcessGroupID{removedProcessGroup}
 			})
 
 			It("should mark the process group for removal", func() {
@@ -293,14 +293,14 @@ var _ = Describe("update_status", func() {
 		})
 
 		When("adding a process group to the ProcessGroupsToRemoveWithoutExclusion list", func() {
-			var removedProcessGroup string
+			var removedProcessGroup fdbv1beta2.ProcessGroupID
 
 			BeforeEach(func() {
 				removedProcessGroup = "storage-1"
 				pods[0].Status.Phase = corev1.PodFailed
 				err = k8sClient.Update(context.TODO(), pods[0])
 				Expect(err).NotTo(HaveOccurred())
-				cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = []string{removedProcessGroup}
+				cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = []fdbv1beta2.ProcessGroupID{removedProcessGroup}
 			})
 
 			It("should be mark the process group for removal without exclusion", func() {
@@ -324,7 +324,7 @@ var _ = Describe("update_status", func() {
 		})
 
 		When("one process group is not reachable", func() {
-			var unreachableProcessGroup string
+			var unreachableProcessGroup fdbv1beta2.ProcessGroupID
 
 			// Must be a JustBeforeEach otherwise the cluster status will result in an error
 			JustBeforeEach(func() {
@@ -386,7 +386,7 @@ var _ = Describe("update_status", func() {
 		})
 
 		When("a Pod is stuck in Pending", func() {
-			var pendingProcessGroup string
+			var pendingProcessGroup fdbv1beta2.ProcessGroupID
 
 			BeforeEach(func() {
 				pendingProcessGroup = podmanager.GetProcessGroupID(cluster, pods[0])
diff --git a/fdbclient/admin_client_test.go b/fdbclient/admin_client_test.go
index 053bab74..c3c0273d 100644
--- a/fdbclient/admin_client_test.go
+++ b/fdbclient/admin_client_test.go
@@ -114,7 +114,7 @@ var _ = Describe("admin_client_test", func() {
 		addr5 := fdbv1beta2.NewProcessAddress(net.ParseIP("127.0.0.5"), "", 0, nil)
 		status := &fdbv1beta2.FoundationDBStatus{
 			Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-				Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+				Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 					"1": {
 						Address:  addr1,
 						Excluded: true,
@@ -674,7 +674,7 @@ protocol fdb00b071010000`,
 			BeforeEach(func() {
 				inputStatus := &fdbv1beta2.FoundationDBStatus{
 					Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-						Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+						Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 							"1": {},
 						},
 					},
@@ -704,7 +704,7 @@ protocol fdb00b071010000`,
 
 			inputStatus := &fdbv1beta2.FoundationDBStatus{
 				Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-					Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+					Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 						"1": {},
 					},
 				},
diff --git a/internal/foundationdb_status_test.go b/internal/foundationdb_status_test.go
index d29b6a98..dd6b2aa9 100644
--- a/internal/foundationdb_status_test.go
+++ b/internal/foundationdb_status_test.go
@@ -49,7 +49,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 				testCase{
 					status: &fdbv1beta2.FoundationDBStatus{
 						Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-							Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+							Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 								"foo": {
 									Locality: map[string]string{
 										fdbv1beta2.FDBLocalityInstanceIDKey: "foo",
@@ -76,7 +76,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 				testCase{
 					status: &fdbv1beta2.FoundationDBStatus{
 						Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-							Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+							Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 								"foo": {
 									Locality: map[string]string{
 										fdbv1beta2.FDBLocalityInstanceIDKey: "foo",
@@ -108,7 +108,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 		)
 	})
 
-	DescribeTable("when getting the minimum uptime and the address map", func(cluster *fdbv1beta2.FoundationDBCluster, status *fdbv1beta2.FoundationDBStatus, useRecoveryState bool, expectedMinimumUptime float64, expectedAddressMap map[string][]fdbv1beta2.ProcessAddress) {
+	DescribeTable("when getting the minimum uptime and the address map", func(cluster *fdbv1beta2.FoundationDBCluster, status *fdbv1beta2.FoundationDBStatus, useRecoveryState bool, expectedMinimumUptime float64, expectedAddressMap map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.ProcessAddress) {
 		minimumUptime, addressMap, err := GetMinimumUptimeAndAddressMap(cluster, status, useRecoveryState)
 		Expect(err).NotTo(HaveOccurred())
 		Expect(minimumUptime).To(BeNumerically("==", expectedMinimumUptime))
@@ -124,7 +124,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 				},
 			}, &fdbv1beta2.FoundationDBStatus{
 				Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-					Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+					Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 						"test": {
 							Address: fdbv1beta2.ProcessAddress{
 								IPAddress: net.ParseIP("127.0.0.1"),
@@ -142,7 +142,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 			},
 			true,
 			30.0,
-			map[string][]fdbv1beta2.ProcessAddress{
+			map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.ProcessAddress{
 				"test": {
 					{
 						IPAddress: net.ParseIP("127.0.0.1"),
@@ -156,7 +156,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 				},
 			}, &fdbv1beta2.FoundationDBStatus{
 				Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-					Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+					Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 						"test": {
 							Address: fdbv1beta2.ProcessAddress{
 								IPAddress: net.ParseIP("127.0.0.1"),
@@ -174,7 +174,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 			},
 			true,
 			90.0,
-			map[string][]fdbv1beta2.ProcessAddress{
+			map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.ProcessAddress{
 				"test": {
 					{
 						IPAddress: net.ParseIP("127.0.0.1"),
@@ -188,7 +188,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 				},
 			}, &fdbv1beta2.FoundationDBStatus{
 				Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-					Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+					Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 						"test": {
 							Address: fdbv1beta2.ProcessAddress{
 								IPAddress: net.ParseIP("127.0.0.1"),
@@ -206,7 +206,7 @@ var _ = Describe("Internal FoundationDBStatus", func() {
 			},
 			false,
 			30.0,
-			map[string][]fdbv1beta2.ProcessAddress{
+			map[fdbv1beta2.ProcessGroupID][]fdbv1beta2.ProcessAddress{
 				"test": {
 					{
 						IPAddress: net.ParseIP("127.0.0.1"),
diff --git a/internal/locality/locality_test.go b/internal/locality/locality_test.go
index 2b6d235f..3b09e728 100644
--- a/internal/locality/locality_test.go
+++ b/internal/locality/locality_test.go
@@ -109,7 +109,7 @@ func generateDefaultStatus(tls bool) *fdbv1beta2.FoundationDBStatus {
 			},
 		},
 		Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-			Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+			Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 				"1": generateDummyProcessInfo("test-1", "dc1", port, tls),
 				"2": generateDummyProcessInfo("test-2", "dc1", port, tls),
 				"3": generateDummyProcessInfo("test-3", "dc1", port, tls),
@@ -711,7 +711,7 @@ var _ = Describe("Change coordinators", func() {
 					ProcessClass: fdbv1beta2.ProcessClassTest,
 				}
 				Expect(testProcess.Address.IsEmpty()).To(BeFalse())
-				status.Cluster.Processes[string(fdbv1beta2.ProcessClassTest)] = testProcess
+				status.Cluster.Processes[fdbv1beta2.ProcessGroupID(fdbv1beta2.ProcessClassTest)] = testProcess
 			})
 
 			It("should ignore the test process and report the coordinators as valid", func() {
diff --git a/internal/pod_models_test.go b/internal/pod_models_test.go
index 5f92acc4..0f35b182 100644
--- a/internal/pod_models_test.go
+++ b/internal/pod_models_test.go
@@ -678,7 +678,7 @@ var _ = Describe("pod_models", func() {
 
 			Context("with an instance that is crash looping", func() {
 				BeforeEach(func() {
-					cluster.Spec.Buggify.CrashLoop = []string{"storage-1"}
+					cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-1"}
 					err := NormalizeClusterSpec(cluster, DeprecationOptions{})
 					Expect(err).NotTo(HaveOccurred())
 					spec, err = GetPodSpec(cluster, fdbv1beta2.ProcessClassStorage, 1)
@@ -697,7 +697,7 @@ var _ = Describe("pod_models", func() {
 					cluster.Spec.Buggify.CrashLoopContainers = []fdbv1beta2.CrashLoopContainerObject{
 						{
 							ContainerName: fdbv1beta2.MainContainerName,
-							Targets:       []string{"storage-1"},
+							Targets:       []fdbv1beta2.ProcessGroupID{"storage-1"},
 						},
 					}
 					err := NormalizeClusterSpec(cluster, DeprecationOptions{})
@@ -924,7 +924,7 @@ var _ = Describe("pod_models", func() {
 
 		Context("with an process group that is crash looping", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"storage-1"}
+				cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-1"}
 				err := NormalizeClusterSpec(cluster, DeprecationOptions{})
 				Expect(err).NotTo(HaveOccurred())
 				spec, err = GetPodSpec(cluster, fdbv1beta2.ProcessClassStorage, 1)
@@ -940,7 +940,7 @@ var _ = Describe("pod_models", func() {
 
 		Context("with all process group crash looping", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"*"}
+				cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"*"}
 				err := NormalizeClusterSpec(cluster, DeprecationOptions{})
 				Expect(err).NotTo(HaveOccurred())
 				spec, err = GetPodSpec(cluster, fdbv1beta2.ProcessClassStorage, 1)
@@ -956,7 +956,7 @@ var _ = Describe("pod_models", func() {
 
 		Context("with a different process group crash looping", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"storage-2"}
+				cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-2"}
 				err := NormalizeClusterSpec(cluster, DeprecationOptions{})
 				Expect(err).NotTo(HaveOccurred())
 				spec, err = GetPodSpec(cluster, fdbv1beta2.ProcessClassStorage, 1)
@@ -980,7 +980,7 @@ var _ = Describe("pod_models", func() {
 				cluster.Spec.Buggify.CrashLoopContainers = []fdbv1beta2.CrashLoopContainerObject{
 					{
 						ContainerName: fdbv1beta2.MainContainerName,
-						Targets:       []string{"storage-2"},
+						Targets:       []fdbv1beta2.ProcessGroupID{"storage-2"},
 					},
 				}
 				spec, err = GetPodSpec(cluster, fdbv1beta2.ProcessClassStorage, 1)
@@ -1004,7 +1004,7 @@ var _ = Describe("pod_models", func() {
 				cluster.Spec.Buggify.CrashLoopContainers = []fdbv1beta2.CrashLoopContainerObject{
 					{
 						ContainerName: fdbv1beta2.MainContainerName,
-						Targets:       []string{"storage-1"},
+						Targets:       []fdbv1beta2.ProcessGroupID{"storage-1"},
 					},
 				}
 				spec, err = GetPodSpec(cluster, fdbv1beta2.ProcessClassStorage, 1)
@@ -1029,7 +1029,7 @@ var _ = Describe("pod_models", func() {
 				cluster.Spec.Buggify.CrashLoopContainers = []fdbv1beta2.CrashLoopContainerObject{
 					{
 						ContainerName: fdbv1beta2.SidecarContainerName,
-						Targets:       []string{"storage-1"},
+						Targets:       []fdbv1beta2.ProcessGroupID{"storage-1"},
 					},
 				}
 				spec, err = GetPodSpec(cluster, fdbv1beta2.ProcessClassStorage, 1)
@@ -1051,7 +1051,7 @@ var _ = Describe("pod_models", func() {
 
 		Context("with an process group with scheduling broken", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.NoSchedule = []string{"storage-1"}
+				cluster.Spec.Buggify.NoSchedule = []fdbv1beta2.ProcessGroupID{"storage-1"}
 				spec, err = GetPodSpec(cluster, fdbv1beta2.ProcessClassStorage, 1)
 				Expect(err).NotTo(HaveOccurred())
 			})
@@ -3159,7 +3159,7 @@ var _ = Describe("pod_models", func() {
 			type testCase struct {
 				container      *corev1.Container
 				initMode       bool
-				processGroupID string
+				processGroupID fdbv1beta2.ProcessGroupID
 				hasError       bool
 			}
 
@@ -3291,14 +3291,14 @@ var _ = Describe("pod_models", func() {
 		})
 	})
 
-	DescribeTable("getting the process group ID from the Pod name", func(cluster *fdbv1beta2.FoundationDBCluster, podName string, expected string) {
+	DescribeTable("getting the process group ID from the Pod name", func(cluster *fdbv1beta2.FoundationDBCluster, podName string, expected fdbv1beta2.ProcessGroupID) {
 		Expect(GetProcessGroupIDFromPodName(cluster, podName)).To(Equal(expected))
 	},
 		Entry("cluster without prefix", &fdbv1beta2.FoundationDBCluster{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: "test",
 			},
-		}, "test-storage-1", "storage-1"),
+		}, "test-storage-1", fdbv1beta2.ProcessGroupID("storage-1")),
 		Entry("cluster with prefix", &fdbv1beta2.FoundationDBCluster{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: "test",
@@ -3306,7 +3306,7 @@ var _ = Describe("pod_models", func() {
 			Spec: fdbv1beta2.FoundationDBClusterSpec{
 				ProcessGroupIDPrefix: "prefix",
 			},
-		}, "test-storage-1", "prefix-storage-1"))
+		}, "test-storage-1", fdbv1beta2.ProcessGroupID("prefix-storage-1")))
 
 	Describe("ContainsPod", func() {
 		var pod1, pod2 *corev1.Pod
diff --git a/internal/removals/remove_test.go b/internal/removals/remove_test.go
index dd7fb5b4..53b95aec 100644
--- a/internal/removals/remove_test.go
+++ b/internal/removals/remove_test.go
@@ -35,7 +35,7 @@ var _ = Describe("remove", func() {
 		BeforeEach(func() {
 			status = &fdbv1beta2.FoundationDBStatus{
 				Cluster: fdbv1beta2.FoundationDBStatusClusterInfo{
-					Processes: map[string]fdbv1beta2.FoundationDBStatusProcessInfo{
+					Processes: map[fdbv1beta2.ProcessGroupID]fdbv1beta2.FoundationDBStatusProcessInfo{
 						"1": {
 							Locality: map[string]string{
 								fdbv1beta2.FDBLocalityInstanceIDKey: "1",
@@ -97,16 +97,16 @@ var _ = Describe("remove", func() {
 			Expect(len(zones)).To(BeNumerically("==", 4))
 
 			Expect(len(zones["zone1"])).To(BeNumerically("==", 2))
-			Expect(zones["zone1"]).To(ConsistOf("1", "2"))
+			Expect(zones["zone1"]).To(ConsistOf(fdbv1beta2.ProcessGroupID("1"), fdbv1beta2.ProcessGroupID("2")))
 
 			Expect(len(zones["zone3"])).To(BeNumerically("==", 1))
-			Expect(zones["zone3"]).To(ConsistOf("3"))
+			Expect(zones["zone3"]).To(ConsistOf(fdbv1beta2.ProcessGroupID("3")))
 
 			Expect(len(zones[UnknownZone])).To(BeNumerically("==", 1))
-			Expect(zones[UnknownZone]).To(ConsistOf("4"))
+			Expect(zones[UnknownZone]).To(ConsistOf(fdbv1beta2.ProcessGroupID("4")))
 
 			Expect(len(zones[TerminatingZone])).To(BeNumerically("==", 2))
-			Expect(zones[TerminatingZone]).To(ConsistOf("5", "6"))
+			Expect(zones[TerminatingZone]).To(ConsistOf(fdbv1beta2.ProcessGroupID("5"), fdbv1beta2.ProcessGroupID("6")))
 
 			Expect(timestamp).To(BeNumerically("==", 42))
 		})
@@ -114,14 +114,14 @@ var _ = Describe("remove", func() {
 	})
 
 	When("getting the process groups to remove", func() {
-		zones := map[string][]string{
+		zones := map[string][]fdbv1beta2.ProcessGroupID{
 			"zone1":     {"1", "2"},
 			"zone3":     {"3", "4"},
 			UnknownZone: {"4", "5"},
 		}
 
 		DescribeTable("should delete the Pods based on the deletion mode",
-			func(removalMode fdbv1beta2.PodUpdateMode, zones map[string][]string, expected int, expectedErr error) {
+			func(removalMode fdbv1beta2.PodUpdateMode, zones map[string][]fdbv1beta2.ProcessGroupID, expected int, expectedErr error) {
 				_, removals, err := GetProcessGroupsToRemove(removalMode, zones)
 				if expectedErr != nil {
 					Expect(err).To(Equal(expectedErr))
@@ -136,7 +136,7 @@ var _ = Describe("remove", func() {
 				nil),
 			Entry("With the deletion mode Zone and only terminating process groupse",
 				fdbv1beta2.PodUpdateModeZone,
-				map[string][]string{
+				map[string][]fdbv1beta2.ProcessGroupID{
 					TerminatingZone: {"1", "2"},
 				},
 				0,
diff --git a/internal/replacements/replacements_test.go b/internal/replacements/replacements_test.go
index 71445e23..faf0f504 100644
--- a/internal/replacements/replacements_test.go
+++ b/internal/replacements/replacements_test.go
@@ -60,7 +60,7 @@ var _ = Describe("replace_misconfigured_pods", func() {
 		JustBeforeEach(func() {
 			processGroupName := fmt.Sprintf("%s-%d", pClass, 1337)
 			status = &fdbv1beta2.ProcessGroupStatus{
-				ProcessGroupID: processGroupName,
+				ProcessGroupID: fdbv1beta2.ProcessGroupID(processGroupName),
 				ProcessClass:   pClass,
 			}
 
@@ -411,7 +411,7 @@ var _ = Describe("replace_misconfigured_pods", func() {
 				pod, err = internal.GetPod(cluster, fdbv1beta2.ProcessClassStorage, 0)
 				Expect(err).NotTo(HaveOccurred())
 				status = &fdbv1beta2.ProcessGroupStatus{
-					ProcessGroupID: fmt.Sprintf("%s-%d", fdbv1beta2.ProcessClassStorage, 1337),
+					ProcessGroupID: fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-%d", fdbv1beta2.ProcessClassStorage, 1337)),
 					ProcessClass:   fdbv1beta2.ProcessClassStorage,
 				}
 
@@ -600,12 +600,12 @@ var _ = Describe("replace_misconfigured_pods", func() {
 	})
 
 	When("using MaxConcurrentMisconfiguredReplacements", func() {
-		var pvcMap map[string]corev1.PersistentVolumeClaim
-		var podMap map[string]*corev1.Pod
+		var pvcMap map[fdbv1beta2.ProcessGroupID]corev1.PersistentVolumeClaim
+		var podMap map[fdbv1beta2.ProcessGroupID]*corev1.Pod
 
 		BeforeEach(func() {
-			pvcMap = map[string]corev1.PersistentVolumeClaim{}
-			podMap = map[string]*corev1.Pod{}
+			pvcMap = map[fdbv1beta2.ProcessGroupID]corev1.PersistentVolumeClaim{}
+			podMap = map[fdbv1beta2.ProcessGroupID]*corev1.Pod{}
 
 			for i := 0; i < 10; i++ {
 				_, id := internal.GetProcessGroupID(cluster, fdbv1beta2.ProcessClassStorage, i)
diff --git a/kubectl-fdb/cmd/buggify_crash_loop_test.go b/kubectl-fdb/cmd/buggify_crash_loop_test.go
index 294f6885..58c81b5c 100644
--- a/kubectl-fdb/cmd/buggify_crash_loop_test.go
+++ b/kubectl-fdb/cmd/buggify_crash_loop_test.go
@@ -34,7 +34,7 @@ var _ = Describe("[plugin] buggify crash-loop instances command", func() {
 		When("adding instances to crash-loop list from a cluster", func() {
 			type testCase struct {
 				Instances                    []string
-				ExpectedInstancesInCrashLoop []string
+				ExpectedInstancesInCrashLoop []fdbv1beta2.ProcessGroupID
 			}
 
 			DescribeTable("should add all targeted processes to crash-loop list",
@@ -54,23 +54,23 @@ var _ = Describe("[plugin] buggify crash-loop instances command", func() {
 				Entry("Adding single instance.",
 					testCase{
 						Instances:                    []string{"test-storage-1"},
-						ExpectedInstancesInCrashLoop: []string{"storage-1"},
+						ExpectedInstancesInCrashLoop: []fdbv1beta2.ProcessGroupID{"storage-1"},
 					}),
 				Entry("Adding multiple instances.",
 					testCase{
 						Instances:                    []string{"test-storage-1", "test-storage-2"},
-						ExpectedInstancesInCrashLoop: []string{"storage-1", "storage-2"},
+						ExpectedInstancesInCrashLoop: []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"},
 					}),
 			)
 
 			When("a process group was already in crash-loop", func() {
 				type testCase struct {
 					Instances                    []string
-					ExpectedInstancesInCrashLoop []string
+					ExpectedInstancesInCrashLoop []fdbv1beta2.ProcessGroupID
 				}
 
 				BeforeEach(func() {
-					cluster.Spec.Buggify.CrashLoop = []string{"storage-1"}
+					cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-1"}
 				})
 
 				DescribeTable("should add all targeted processes to crash-loop list",
@@ -90,17 +90,17 @@ var _ = Describe("[plugin] buggify crash-loop instances command", func() {
 					Entry("Adding the same instance.",
 						testCase{
 							Instances:                    []string{"test-storage-1"},
-							ExpectedInstancesInCrashLoop: []string{"storage-1"},
+							ExpectedInstancesInCrashLoop: []fdbv1beta2.ProcessGroupID{"storage-1"},
 						}),
 					Entry("Adding different instance.",
 						testCase{
 							Instances:                    []string{"test-storage-2"},
-							ExpectedInstancesInCrashLoop: []string{"storage-1", "storage-2"},
+							ExpectedInstancesInCrashLoop: []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"},
 						}),
 					Entry("Adding multiple instances.",
 						testCase{
 							Instances:                    []string{"test-storage-2", "test-storage-3"},
-							ExpectedInstancesInCrashLoop: []string{"storage-1", "storage-2", "storage-3"},
+							ExpectedInstancesInCrashLoop: []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2", "storage-3"},
 						}),
 				)
 			})
@@ -108,12 +108,12 @@ var _ = Describe("[plugin] buggify crash-loop instances command", func() {
 
 		When("removing instances from crash-loop list from a cluster", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"storage-1", "storage-2", "storage-3"}
+				cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2", "storage-3"}
 			})
 
 			type testCase struct {
 				Instances                    []string
-				ExpectedInstancesInCrashLoop []string
+				ExpectedInstancesInCrashLoop []fdbv1beta2.ProcessGroupID
 			}
 
 			DescribeTable("should remove all targeted processes from the crash-loop list",
@@ -133,20 +133,19 @@ var _ = Describe("[plugin] buggify crash-loop instances command", func() {
 				Entry("Removing single instance.",
 					testCase{
 						Instances:                    []string{"test-storage-1"},
-						ExpectedInstancesInCrashLoop: []string{"storage-2", "storage-3"},
+						ExpectedInstancesInCrashLoop: []fdbv1beta2.ProcessGroupID{"storage-2", "storage-3"},
 					}),
 				Entry("Removing multiple instances.",
 					testCase{
 						Instances:                    []string{"test-storage-2", "test-storage-3"},
-						ExpectedInstancesInCrashLoop: []string{"storage-1"},
+						ExpectedInstancesInCrashLoop: []fdbv1beta2.ProcessGroupID{"storage-1"},
 					}),
 			)
-
 		})
 
 		When("clearing crash-loop list", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.CrashLoop = []string{"storage-1", "storage-2", "storage-3"}
+				cluster.Spec.Buggify.CrashLoop = []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2", "storage-3"}
 			})
 
 			It("should clear the crash-loop list", func() {
diff --git a/kubectl-fdb/cmd/buggify_no_schedule_test.go b/kubectl-fdb/cmd/buggify_no_schedule_test.go
index e2ef4628..452704c4 100644
--- a/kubectl-fdb/cmd/buggify_no_schedule_test.go
+++ b/kubectl-fdb/cmd/buggify_no_schedule_test.go
@@ -34,7 +34,7 @@ var _ = Describe("[plugin] buggify no-schedule instances command", func() {
 		When("adding instances to no-schedule list from a cluster", func() {
 			type testCase struct {
 				Instances                     []string
-				ExpectedInstancesInNoSchedule []string
+				ExpectedInstancesInNoSchedule []fdbv1beta2.ProcessGroupID
 			}
 
 			DescribeTable("should add all targeted processes to no-schedule list",
@@ -54,23 +54,23 @@ var _ = Describe("[plugin] buggify no-schedule instances command", func() {
 				Entry("Adding single instance.",
 					testCase{
 						Instances:                     []string{"test-storage-1"},
-						ExpectedInstancesInNoSchedule: []string{"storage-1"},
+						ExpectedInstancesInNoSchedule: []fdbv1beta2.ProcessGroupID{"storage-1"},
 					}),
 				Entry("Adding multiple instances.",
 					testCase{
 						Instances:                     []string{"test-storage-1", "test-storage-2"},
-						ExpectedInstancesInNoSchedule: []string{"storage-1", "storage-2"},
+						ExpectedInstancesInNoSchedule: []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"},
 					}),
 			)
 
 			When("a process group is already in no-schedule", func() {
 				BeforeEach(func() {
-					cluster.Spec.Buggify.NoSchedule = []string{"storage-1"}
+					cluster.Spec.Buggify.NoSchedule = []fdbv1beta2.ProcessGroupID{"storage-1"}
 				})
 
 				type testCase struct {
 					Instances                     []string
-					ExpectedInstancesInNoSchedule []string
+					ExpectedInstancesInNoSchedule []fdbv1beta2.ProcessGroupID
 				}
 
 				DescribeTable("should add all targeted processes to no-schedule list",
@@ -90,17 +90,17 @@ var _ = Describe("[plugin] buggify no-schedule instances command", func() {
 					Entry("Adding the same instance.",
 						testCase{
 							Instances:                     []string{"test-storage-1"},
-							ExpectedInstancesInNoSchedule: []string{"storage-1"},
+							ExpectedInstancesInNoSchedule: []fdbv1beta2.ProcessGroupID{"storage-1"},
 						}),
 					Entry("Adding different instance.",
 						testCase{
 							Instances:                     []string{"test-storage-2"},
-							ExpectedInstancesInNoSchedule: []string{"storage-1", "storage-2"},
+							ExpectedInstancesInNoSchedule: []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"},
 						}),
 					Entry("Adding multiple instances.",
 						testCase{
 							Instances:                     []string{"test-storage-2", "test-storage-3"},
-							ExpectedInstancesInNoSchedule: []string{"storage-1", "storage-2", "storage-3"},
+							ExpectedInstancesInNoSchedule: []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2", "storage-3"},
 						}),
 				)
 			})
@@ -108,12 +108,12 @@ var _ = Describe("[plugin] buggify no-schedule instances command", func() {
 
 		When("removing process group from no-schedule list from a cluster", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.NoSchedule = []string{"storage-1", "storage-2", "storage-3"}
+				cluster.Spec.Buggify.NoSchedule = []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2", "storage-3"}
 			})
 
 			type testCase struct {
 				Instances                     []string
-				ExpectedInstancesInNoSchedule []string
+				ExpectedInstancesInNoSchedule []fdbv1beta2.ProcessGroupID
 			}
 
 			DescribeTable("should remove all targeted processes from the no-schedule list",
@@ -133,12 +133,12 @@ var _ = Describe("[plugin] buggify no-schedule instances command", func() {
 				Entry("Removing single instance.",
 					testCase{
 						Instances:                     []string{"test-storage-1"},
-						ExpectedInstancesInNoSchedule: []string{"storage-2", "storage-3"},
+						ExpectedInstancesInNoSchedule: []fdbv1beta2.ProcessGroupID{"storage-2", "storage-3"},
 					}),
 				Entry("Removing multiple instances.",
 					testCase{
 						Instances:                     []string{"test-storage-2", "test-storage-3"},
-						ExpectedInstancesInNoSchedule: []string{"storage-1"},
+						ExpectedInstancesInNoSchedule: []fdbv1beta2.ProcessGroupID{"storage-1"},
 					}),
 			)
 
@@ -146,7 +146,7 @@ var _ = Describe("[plugin] buggify no-schedule instances command", func() {
 
 		When("clearing no-schedule list", func() {
 			BeforeEach(func() {
-				cluster.Spec.Buggify.NoSchedule = []string{"storage-1", "storage-2", "storage-3"}
+				cluster.Spec.Buggify.NoSchedule = []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2", "storage-3"}
 			})
 
 			It("should clear the no-schedule list", func() {
diff --git a/kubectl-fdb/cmd/cordon_test.go b/kubectl-fdb/cmd/cordon_test.go
index fa141081..3fb655a3 100644
--- a/kubectl-fdb/cmd/cordon_test.go
+++ b/kubectl-fdb/cmd/cordon_test.go
@@ -41,8 +41,8 @@ var _ = Describe("[plugin] cordon command", func() {
 		type testCase struct {
 			nodes                                     []string
 			WithExclusion                             bool
-			ExpectedInstancesToRemove                 []string
-			ExpectedInstancesToRemoveWithoutExclusion []string
+			ExpectedInstancesToRemove                 []fdbv1beta2.ProcessGroupID
+			ExpectedInstancesToRemoveWithoutExclusion []fdbv1beta2.ProcessGroupID
 			clusterName                               string
 			clusterLabel                              string
 		}
@@ -69,8 +69,8 @@ var _ = Describe("[plugin] cordon command", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				clusterNames := []string{clusterName, secondClusterName}
-				var instancesToRemove []string
-				var instancesToRemoveWithoutExclusion []string
+				var instancesToRemove []fdbv1beta2.ProcessGroupID
+				var instancesToRemoveWithoutExclusion []fdbv1beta2.ProcessGroupID
 				for _, clusterName := range clusterNames {
 					var resCluster fdbv1beta2.FoundationDBCluster
 					err = k8sClient.Get(context.Background(), client.ObjectKey{
@@ -89,8 +89,8 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{"node-1"},
 					WithExclusion:             true,
-					ExpectedInstancesToRemove: []string{fmt.Sprintf("%s-instance-1", clusterName)},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{},
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", clusterName))},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 					clusterName:  clusterName,
 					clusterLabel: "",
 				}),
@@ -98,8 +98,8 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{"node-1"},
 					WithExclusion:             false,
-					ExpectedInstancesToRemove: []string{},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{fmt.Sprintf("%s-instance-1", clusterName)},
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", clusterName))},
 					clusterName:  clusterName,
 					clusterLabel: "",
 				}),
@@ -107,8 +107,8 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{""},
 					WithExclusion:             true,
-					ExpectedInstancesToRemove: []string{},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{},
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 					clusterName:  clusterName,
 					clusterLabel: "",
 				}),
@@ -116,8 +116,8 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{""},
 					WithExclusion:             false,
-					ExpectedInstancesToRemove: []string{},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{},
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 					clusterName:  clusterName,
 					clusterLabel: "",
 				}),
@@ -125,11 +125,11 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:         []string{"node-1", "node-2"},
 					WithExclusion: true,
-					ExpectedInstancesToRemove: []string{
-						fmt.Sprintf("%s-instance-1", clusterName),
-						fmt.Sprintf("%s-instance-2", clusterName),
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", clusterName)),
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-2", clusterName)),
 					},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 					clusterName:  clusterName,
 					clusterLabel: "",
 				}),
@@ -137,10 +137,10 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{"node-1", "node-2"},
 					WithExclusion:             false,
-					ExpectedInstancesToRemove: []string{},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{
-						fmt.Sprintf("%s-instance-1", clusterName),
-						fmt.Sprintf("%s-instance-2", clusterName),
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", clusterName)),
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-2", clusterName)),
 					},
 					clusterName:  clusterName,
 					clusterLabel: "",
@@ -149,8 +149,8 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{"node-1"},
 					WithExclusion:             true,
-					ExpectedInstancesToRemove: []string{fmt.Sprintf("%s-instance-1", secondClusterName)},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{},
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", secondClusterName))},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 					clusterName:  secondClusterName,
 					clusterLabel: "",
 				}),
@@ -158,8 +158,8 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{"node-1"},
 					WithExclusion:             true,
-					ExpectedInstancesToRemove: []string{fmt.Sprintf("%s-instance-1", secondClusterName)},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{},
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", secondClusterName))},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 					clusterName:  secondClusterName,
 					clusterLabel: fdbv1beta2.FDBClusterLabel,
 				}),
@@ -167,11 +167,11 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:         []string{"node-1"},
 					WithExclusion: true,
-					ExpectedInstancesToRemove: []string{
-						fmt.Sprintf("%s-instance-1", clusterName),
-						fmt.Sprintf("%s-instance-1", secondClusterName),
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", clusterName)),
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", secondClusterName)),
 					},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 					clusterName:  "",
 					clusterLabel: fdbv1beta2.FDBClusterLabel,
 				}),
@@ -179,12 +179,12 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{"node-1", "node-2"},
 					WithExclusion:             false,
-					ExpectedInstancesToRemove: []string{},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{
-						fmt.Sprintf("%s-instance-1", clusterName),
-						fmt.Sprintf("%s-instance-2", clusterName),
-						fmt.Sprintf("%s-instance-1", secondClusterName),
-						fmt.Sprintf("%s-instance-2", secondClusterName),
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", clusterName)),
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-2", clusterName)),
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-1", secondClusterName)),
+						fdbv1beta2.ProcessGroupID(fmt.Sprintf("%s-instance-2", secondClusterName)),
 					},
 					clusterName:  "",
 					clusterLabel: fdbv1beta2.FDBClusterLabel,
@@ -193,8 +193,8 @@ var _ = Describe("[plugin] cordon command", func() {
 				testCase{
 					nodes:                     []string{""},
 					WithExclusion:             false,
-					ExpectedInstancesToRemove: []string{},
-					ExpectedInstancesToRemoveWithoutExclusion: []string{},
+					ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{},
+					ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 					clusterName:  "",
 					clusterLabel: fdbv1beta2.FDBClusterLabel,
 				}),
diff --git a/kubectl-fdb/cmd/fix_coordinator_ips_test.go b/kubectl-fdb/cmd/fix_coordinator_ips_test.go
index d753b40a..59f6c418 100644
--- a/kubectl-fdb/cmd/fix_coordinator_ips_test.go
+++ b/kubectl-fdb/cmd/fix_coordinator_ips_test.go
@@ -116,7 +116,7 @@ var _ = Describe("[plugin] fix-coordinator-ips command", func() {
 			Context                  string
 			ExpectedConnectionString string
 			ExpectedError            string
-			AddressUpdates           map[string]string
+			AddressUpdates           map[fdbv1beta2.ProcessGroupID]string
 		}
 
 		BeforeEach(func() {
@@ -173,7 +173,7 @@ var _ = Describe("[plugin] fix-coordinator-ips command", func() {
 			),
 			Entry("updated address",
 				testCase{
-					AddressUpdates: map[string]string{
+					AddressUpdates: map[fdbv1beta2.ProcessGroupID]string{
 						"storage-1": "127.0.1.1",
 						"storage-2": "127.0.1.2",
 						"storage-5": "127.0.1.5",
@@ -183,7 +183,7 @@ var _ = Describe("[plugin] fix-coordinator-ips command", func() {
 			),
 			Entry("IP address with no process group",
 				testCase{
-					AddressUpdates: map[string]string{
+					AddressUpdates: map[fdbv1beta2.ProcessGroupID]string{
 						"storage-1": "",
 					},
 					ExpectedConnectionString: "test:asdfkjh@127.0.0.1:4501,127.0.0.2:4501,127.0.0.3:4501",
diff --git a/kubectl-fdb/cmd/k8s_client_test.go b/kubectl-fdb/cmd/k8s_client_test.go
index 9ff34c74..9377c5b6 100644
--- a/kubectl-fdb/cmd/k8s_client_test.go
+++ b/kubectl-fdb/cmd/k8s_client_test.go
@@ -176,7 +176,7 @@ var _ = Describe("[plugin] using the Kubernetes client", func() {
 	When("getting the process groups IDs from Pods", func() {
 		When("the cluster doesn't have a prefix", func() {
 			DescribeTable("should get all process groups IDs",
-				func(podNames []string, expected []string) {
+				func(podNames []string, expected []fdbv1beta2.ProcessGroupID) {
 					instances, err := getProcessGroupIDsFromPodName(cluster, podNames)
 					Expect(err).NotTo(HaveOccurred())
 					Expect(instances).To(ContainElements(expected))
@@ -184,15 +184,15 @@ var _ = Describe("[plugin] using the Kubernetes client", func() {
 				},
 				Entry("Filter one instance",
 					[]string{"test-storage-1"},
-					[]string{"storage-1"},
+					[]fdbv1beta2.ProcessGroupID{"storage-1"},
 				),
 				Entry("Filter two instances",
 					[]string{"test-storage-1", "test-storage-2"},
-					[]string{"storage-1", "storage-2"},
+					[]fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"},
 				),
 				Entry("Filter no instance",
 					[]string{""},
-					[]string{},
+					[]fdbv1beta2.ProcessGroupID{},
 				),
 			)
 		})
@@ -214,7 +214,7 @@ var _ = Describe("[plugin] using the Kubernetes client", func() {
 			})
 
 			DescribeTable("should get all process groups IDs",
-				func(podNames []string, expected []string) {
+				func(podNames []string, expected []fdbv1beta2.ProcessGroupID) {
 					instances, err := getProcessGroupIDsFromPodName(cluster, podNames)
 					Expect(err).NotTo(HaveOccurred())
 					Expect(instances).To(ContainElements(expected))
@@ -222,15 +222,15 @@ var _ = Describe("[plugin] using the Kubernetes client", func() {
 				},
 				Entry("Filter one instance",
 					[]string{"test-storage-1"},
-					[]string{"banana-storage-1"},
+					[]fdbv1beta2.ProcessGroupID{"banana-storage-1"},
 				),
 				Entry("Filter two instances",
 					[]string{"test-storage-1", "test-storage-2"},
-					[]string{"banana-storage-1", "banana-storage-2"},
+					[]fdbv1beta2.ProcessGroupID{"banana-storage-1", "banana-storage-2"},
 				),
 				Entry("Filter no instance",
 					[]string{""},
-					[]string{},
+					[]fdbv1beta2.ProcessGroupID{},
 				),
 			)
 		})
diff --git a/kubectl-fdb/cmd/remove_process_group_test.go b/kubectl-fdb/cmd/remove_process_group_test.go
index 1cff83ab..4f521e46 100644
--- a/kubectl-fdb/cmd/remove_process_group_test.go
+++ b/kubectl-fdb/cmd/remove_process_group_test.go
@@ -52,8 +52,8 @@ var _ = Describe("[plugin] remove process groups command", func() {
 			type testCase struct {
 				Instances                                 []string
 				WithExclusion                             bool
-				ExpectedInstancesToRemove                 []string
-				ExpectedInstancesToRemoveWithoutExclusion []string
+				ExpectedInstancesToRemove                 []fdbv1beta2.ProcessGroupID
+				ExpectedInstancesToRemoveWithoutExclusion []fdbv1beta2.ProcessGroupID
 				ExpectedProcessCounts                     fdbv1beta2.ProcessCounts
 				RemoveAllFailed                           bool
 			}
@@ -79,8 +79,8 @@ var _ = Describe("[plugin] remove process groups command", func() {
 					testCase{
 						Instances:                 []string{"test-storage-1"},
 						WithExclusion:             true,
-						ExpectedInstancesToRemove: []string{"storage-1"},
-						ExpectedInstancesToRemoveWithoutExclusion: []string{},
+						ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{"storage-1"},
+						ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 						ExpectedProcessCounts: fdbv1beta2.ProcessCounts{
 							Storage: 1,
 						},
@@ -90,8 +90,8 @@ var _ = Describe("[plugin] remove process groups command", func() {
 					testCase{
 						Instances:                 []string{"test-storage-1"},
 						WithExclusion:             false,
-						ExpectedInstancesToRemove: []string{},
-						ExpectedInstancesToRemoveWithoutExclusion: []string{"storage-1"},
+						ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{},
+						ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{"storage-1"},
 						ExpectedProcessCounts: fdbv1beta2.ProcessCounts{
 							Storage: 1,
 						},
@@ -100,8 +100,8 @@ var _ = Describe("[plugin] remove process groups command", func() {
 					testCase{
 						Instances:                 []string{"test-storage-42"},
 						WithExclusion:             true,
-						ExpectedInstancesToRemove: []string{"storage-42"},
-						ExpectedInstancesToRemoveWithoutExclusion: []string{},
+						ExpectedInstancesToRemove: []fdbv1beta2.ProcessGroupID{"storage-42"},
+						ExpectedInstancesToRemoveWithoutExclusion: []fdbv1beta2.ProcessGroupID{},
 						ExpectedProcessCounts: fdbv1beta2.ProcessCounts{
 							Storage: 1,
 						},
@@ -111,7 +111,7 @@ var _ = Describe("[plugin] remove process groups command", func() {
 
 			When("a process group was already marked for removal", func() {
 				BeforeEach(func() {
-					cluster.Spec.ProcessGroupsToRemove = []string{"storage-1"}
+					cluster.Spec.ProcessGroupsToRemove = []fdbv1beta2.ProcessGroupID{"storage-1"}
 				})
 
 				When("adding the same process group to the removal list without exclusion", func() {
@@ -127,9 +127,9 @@ var _ = Describe("[plugin] remove process groups command", func() {
 						}, &resCluster)
 
 						Expect(err).NotTo(HaveOccurred())
-						Expect(resCluster.Spec.ProcessGroupsToRemove).To(ContainElements("storage-1"))
+						Expect(resCluster.Spec.ProcessGroupsToRemove).To(ContainElements(fdbv1beta2.ProcessGroupID("storage-1")))
 						Expect(len(resCluster.Spec.ProcessGroupsToRemove)).To(BeNumerically("==", len(removals)))
-						Expect(resCluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements("storage-1"))
+						Expect(resCluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(fdbv1beta2.ProcessGroupID("storage-1")))
 						Expect(len(resCluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(BeNumerically("==", len(removals)))
 					})
 				})
@@ -147,7 +147,7 @@ var _ = Describe("[plugin] remove process groups command", func() {
 						}, &resCluster)
 
 						Expect(err).NotTo(HaveOccurred())
-						Expect(resCluster.Spec.ProcessGroupsToRemove).To(ContainElements("storage-1"))
+						Expect(resCluster.Spec.ProcessGroupsToRemove).To(ContainElements(fdbv1beta2.ProcessGroupID("storage-1")))
 						Expect(len(resCluster.Spec.ProcessGroupsToRemove)).To(BeNumerically("==", len(removals)))
 						Expect(len(resCluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(BeNumerically("==", 0))
 					})
diff --git a/pkg/fdbadminclient/mock/lock_client_test.go b/pkg/fdbadminclient/mock/lock_client_test.go
index 739c16c6..33fda9d7 100644
--- a/pkg/fdbadminclient/mock/lock_client_test.go
+++ b/pkg/fdbadminclient/mock/lock_client_test.go
@@ -48,16 +48,16 @@ var _ = Describe("lock_client_test", func() {
 
 	Describe("AddPendingUpgrades", func() {
 		It("adds the upgrades to the map", func() {
-			err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.Default, []string{"storage-1", "storage-2"})
+			err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.Default, []fdbv1beta2.ProcessGroupID{"storage-1", "storage-2"})
 			Expect(err).NotTo(HaveOccurred())
 
-			err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.Default, []string{"storage-3"})
+			err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.Default, []fdbv1beta2.ProcessGroupID{"storage-3"})
 			Expect(err).NotTo(HaveOccurred())
 
-			err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.NextMajorVersion, []string{"storage-3", "storage-4"})
+			err = lockClient.AddPendingUpgrades(fdbv1beta2.Versions.NextMajorVersion, []fdbv1beta2.ProcessGroupID{"storage-3", "storage-4"})
 			Expect(err).NotTo(HaveOccurred())
 
-			Expect(lockClient.pendingUpgrades).To(Equal(map[fdbv1beta2.Version]map[string]bool{
+			Expect(lockClient.pendingUpgrades).To(Equal(map[fdbv1beta2.Version]map[fdbv1beta2.ProcessGroupID]bool{
 				fdbv1beta2.Versions.Default: {
 					"storage-1": true,
 					"storage-2": true,
@@ -73,7 +73,7 @@ var _ = Describe("lock_client_test", func() {
 
 	Describe("GetPendingUpgrades", func() {
 		BeforeEach(func() {
-			lockClient.pendingUpgrades = map[fdbv1beta2.Version]map[string]bool{
+			lockClient.pendingUpgrades = map[fdbv1beta2.Version]map[fdbv1beta2.ProcessGroupID]bool{
 				fdbv1beta2.Versions.Default: {
 					"storage-1": true,
 					"storage-2": true,
@@ -90,7 +90,7 @@ var _ = Describe("lock_client_test", func() {
 			It("returns the upgrades in the map", func() {
 				upgrades, err := lockClient.GetPendingUpgrades(fdbv1beta2.Versions.Default)
 				Expect(err).NotTo(HaveOccurred())
-				Expect(upgrades).To(Equal(map[string]bool{
+				Expect(upgrades).To(Equal(map[fdbv1beta2.ProcessGroupID]bool{
 					"storage-1": true,
 					"storage-2": true,
 					"storage-3": true,