Repository: gardener/machine-controller-manager

Issue:
Machine creation retry too frequently for machines with `ResourceExhausted`
**How to categorize this issue?**
/area robustness
/kind enhancement
/priority 3

**What would you like to be added**:

- Adding a check for `codes.ResourceExhausted` with a higher retry period.

https://github.com/gardener/machine-controller-manager/blob/1e2563f85f5b6912aeb7ea42b5c5264ca67d667a/pkg/util/provider/machinecontroller/machine_util.go#L503-L515

**Why is this needed**:
Currently, machines that fail due to `codes.ResourceExhaused`, are retried using `machineutils.MediumRetry` which is every 3 minutes.
When a resource in the underlying infrastructure is exhausted, it's unlikely that this will change in that short period of time.

The `provider-openstack` first creates the `volume` and then the `machine`.
However, depending on the size of the `nodePool`, this can lead to a large number of unnecessary create/delete API calls.

Hints from issue comments:

Solution patch:
diff --git a/pkg/apis/machine/types.go b/pkg/apis/machine/types.go
index ea6cfc07b..d0ff57388 100644
--- a/pkg/apis/machine/types.go
+++ b/pkg/apis/machine/types.go
@@ -221,6 +221,15 @@ const (
 
 	// MachineCrashLoopBackOff means creation or deletion of the machine is failing. It means that machine object is present but there is no corresponding VM.
 	MachineCrashLoopBackOff MachinePhase = "CrashLoopBackOff"
+
+	// MachineInPlaceUpdating means machine is being updated in-place
+	MachineInPlaceUpdating MachinePhase = "InPlaceUpdating"
+
+	// MachineInPlaceUpdateSuccessful means machine in-place update was successful
+	MachineInPlaceUpdateSuccessful MachinePhase = "InPlaceUpdateSuccessful"
+
+	// MachineInPlaceUpdateFailed means machine in-place update failed
+	MachineInPlaceUpdateFailed MachinePhase = "InPlaceUpdateFailed"
 )
 
 // MachineState is a label for the state of a machines at the current time.
@@ -249,6 +258,12 @@ const (
 	// MachineOperationUpdate indicates that the operation was an update
 	MachineOperationUpdate MachineOperationType = "Update"
 
+	// MachineOperationInPlaceUpdate indicates that the operation was an in-place update
+	MachineOperationInPlaceUpdate MachineOperationType = "InPlaceUpdate"
+
+	// MachineOperationDrainNode indicates that the operation was a drain node
+	MachineOperationDrainNode MachineOperationType = "DrainNode"
+
 	// MachineOperationHealthCheck indicates that the operation was a create
 	MachineOperationHealthCheck MachineOperationType = "HealthCheck"
 
@@ -271,6 +286,29 @@ const (
 	ConditionUnknown ConditionStatus = "Unknown"
 )
 
+const (
+	// NodeInPlaceUpdate is a node condition type for in-place update
+	NodeInPlaceUpdate corev1.NodeConditionType = "InPlaceUpdate"
+
+	// CandidateForUpdate is a constant for reason in condition that indicates node is candidate for update
+	CandidateForUpdate string = "CandidateForUpdate"
+
+	// SelectedForUpdate is a constant for reason in condition that indicates node is selected for update
+	SelectedForUpdate string = "SelectedForUpdate"
+
+	// DrainSuccessful is a constant for reason in condition that indicates node drain is successful
+	DrainSuccessful string = "DrainSuccessful"
+
+	// ReadyForUpdate is a constant for reason in condition that indicates node is ready for update
+	ReadyForUpdate string = "ReadyForUpdate"
+
+	// UpdateSuccessful is a constant for reason in condition that indicates update succeeded
+	UpdateSuccessful string = "UpdateSuccessful"
+
+	// UpdateFailed is a constant for reason in condition that indicates update failed
+	UpdateFailed string = "UpdateFailed"
+)
+
 /********************** MachineSet APIs ***************/
 
 // +genclient
@@ -615,8 +653,8 @@ const (
 	// or deleted.
 	MachineDeploymentReplicaFailure MachineDeploymentConditionType = "ReplicaFailure"
 
-	// MachineDeploymentFrozen is added in a MachineDeployment when one of its machines fails to be created
-	// or deleted.
+	// MachineDeploymentFrozen is added in a MachineDeployment when one of its
+	// machineSet has either the "freeze" label or the "Frozen" condition on it.
 	MachineDeploymentFrozen MachineDeploymentConditionType = "Frozen"
 )
 
diff --git a/pkg/apis/machine/v1alpha1/machine_types.go b/pkg/apis/machine/v1alpha1/machine_types.go
index b5b89188e..ffc381437 100644
--- a/pkg/apis/machine/v1alpha1/machine_types.go
+++ b/pkg/apis/machine/v1alpha1/machine_types.go
@@ -151,6 +151,15 @@ const (
 
 	// MachineCrashLoopBackOff means machine creation is failing. It means that machine object is present but there is no corresponding VM.
 	MachineCrashLoopBackOff MachinePhase = "CrashLoopBackOff"
+
+	// MachineInPlaceUpdating means machine is being updated in-place
+	MachineInPlaceUpdating MachinePhase = "InPlaceUpdating"
+
+	// MachineInPlaceUpdateSuccessful means machine in-place update was successful
+	MachineInPlaceUpdateSuccessful MachinePhase = "InPlaceUpdateSuccessful"
+
+	// MachineInPlaceUpdateFailed means machine in-place update failed
+	MachineInPlaceUpdateFailed MachinePhase = "InPlaceUpdateFailed"
 )
 
 // MachineState is a current state of the operation.
@@ -179,6 +188,12 @@ const (
 	// MachineOperationUpdate indicates that the operation was an update
 	MachineOperationUpdate MachineOperationType = "Update"
 
+	// MachineOperationInPlaceUpdate indicates that the operation was an in-place update
+	MachineOperationInPlaceUpdate MachineOperationType = "InPlaceUpdate"
+
+	// MachineOperationDrainNode indicates that the operation was a drain node
+	MachineOperationDrainNode MachineOperationType = "DrainNode"
+
 	// MachineOperationHealthCheck indicates that the operation was a health check of node object
 	MachineOperationHealthCheck MachineOperationType = "HealthCheck"
 
@@ -201,6 +216,29 @@ const (
 	ConditionUnknown ConditionStatus = "Unknown"
 )
 
+const (
+	// NodeInPlaceUpdate is a node condition type for in-place update
+	NodeInPlaceUpdate corev1.NodeConditionType = "InPlaceUpdate"
+
+	// CandidateForUpdate is a constant for reason in condition that indicates node is candidate for update
+	CandidateForUpdate string = "CandidateForUpdate"
+
+	// SelectedForUpdate is a constant for reason in condition that indicates node is selected for update
+	SelectedForUpdate string = "SelectedForUpdate"
+
+	// ReadyForUpdate is a constant for reason in condition that indicates node is ready for update
+	ReadyForUpdate string = "ReadyForUpdate"
+
+	// DrainSuccessful is a constant for reason in condition that indicates node drain is successful
+	DrainSuccessful string = "DrainSuccessful"
+
+	// UpdateSuccessful is a constant for reason in condition that indicates update succeeded
+	UpdateSuccessful string = "UpdateSuccessful"
+
+	// UpdateFailed is a constant for reason in condition that indicates update failed
+	UpdateFailed string = "UpdateFailed"
+)
+
 // CurrentStatus contains information about the current status of Machine.
 type CurrentStatus struct {
 	Phase MachinePhase `json:"phase,omitempty"`
diff --git a/pkg/apis/machine/v1alpha1/machinedeployment_types.go b/pkg/apis/machine/v1alpha1/machinedeployment_types.go
index edc01e284..1839ad866 100644
--- a/pkg/apis/machine/v1alpha1/machinedeployment_types.go
+++ b/pkg/apis/machine/v1alpha1/machinedeployment_types.go
@@ -249,8 +249,8 @@ const (
 	// or deleted.
 	MachineDeploymentReplicaFailure MachineDeploymentConditionType = "ReplicaFailure"
 
-	// MachineDeploymentFrozen is added in a MachineDeployment when one of its machines fails to be created
-	// or deleted.
+	// MachineDeploymentFrozen is added in a MachineDeployment when one of its
+	// machineSet has either the "freeze" label or the "Frozen" condition on it.
 	MachineDeploymentFrozen MachineDeploymentConditionType = "Frozen"
 )
 
diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go
index 6d876557b..10d3efc38 100644
--- a/pkg/controller/controller.go
+++ b/pkg/controller/controller.go
@@ -119,6 +119,7 @@ func NewController(
 
 	// MachineDeployment Controller Informers
 	_, _ = machineInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+		UpdateFunc: controller.updateMachineToMachineDeployment,
 		DeleteFunc: controller.deleteMachineDeployment,
 	})
 
diff --git a/pkg/controller/deployment.go b/pkg/controller/deployment.go
index 862723d02..fcfc7d663 100644
--- a/pkg/controller/deployment.go
+++ b/pkg/controller/deployment.go
@@ -25,26 +25,25 @@ package controller
 import (
 	"context"
 	"fmt"
-	"github.com/gardener/machine-controller-manager/pkg/util/annotations"
-	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"reflect"
 	"sync"
 	"time"
 
-	"k8s.io/klog/v2"
-
 	v1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/types"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/client-go/tools/cache"
+	"k8s.io/klog/v2"
 
 	"github.com/gardener/machine-controller-manager/pkg/apis/machine"
 	"github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
 	"github.com/gardener/machine-controller-manager/pkg/apis/machine/validation"
+	"github.com/gardener/machine-controller-manager/pkg/util/annotations"
+	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
 )
 
 // controllerKind contains the schema.GroupVersionKind for this controller type.
@@ -223,6 +222,32 @@ func (dc *controller) deleteMachineSetToDeployment(obj interface{}) {
 	dc.enqueueMachineDeployment(d)
 }
 
+// updateMachineToMachineDeployment will enqueue the machine deployment if the machine InPlaceUpdate node condition changes to UpdateSuccessful.
+func (dc *controller) updateMachineToMachineDeployment(old, cur any) {
+	oldMachine, ok := old.(*v1alpha1.Machine)
+	if !ok {
+		return
+	}
+
+	curMachine, ok := cur.(*v1alpha1.Machine)
+	if !ok {
+		return
+	}
+
+	oldMachineCondition := getMachineCondition(oldMachine, v1alpha1.NodeInPlaceUpdate)
+	currMachineCondition := getMachineCondition(curMachine, v1alpha1.NodeInPlaceUpdate)
+
+	oldMachineConditionReasonUpdateSuccessful := oldMachineCondition != nil && oldMachineCondition.Reason == v1alpha1.UpdateSuccessful
+	currMachineConditionReasonUpdateSuccessful := currMachineCondition != nil && currMachineCondition.Reason == v1alpha1.UpdateSuccessful
+
+	if !oldMachineConditionReasonUpdateSuccessful && currMachineConditionReasonUpdateSuccessful {
+		d := dc.getMachineDeploymentForMachine(curMachine)
+		if d != nil {
+			dc.enqueueMachineDeployment(d)
+		}
+	}
+}
+
 // deleteMachine will enqueue a Recreate Deployment once all of its Machines have stopped running.
 func (dc *controller) deleteMachineToMachineDeployment(ctx context.Context, obj interface{}) {
 	machine, ok := obj.(*v1alpha1.Machine)
@@ -244,7 +269,7 @@ func (dc *controller) deleteMachineToMachineDeployment(ctx context.Context, obj
 		}
 	}
 	klog.V(4).Infof("Machine %s deleted.", machine.Name)
-	if d := dc.getMachineDeploymentForMachine(ctx, machine); d != nil && d.Spec.Strategy.Type == v1alpha1.RecreateMachineDeploymentStrategyType {
+	if d := dc.getMachineDeploymentForMachine(machine); d != nil && d.Spec.Strategy.Type == v1alpha1.RecreateMachineDeploymentStrategyType {
 		// Sync if this Deployment now has no more Machines.
 		machineSets, err := ListMachineSets(d, IsListFromClient(ctx, dc.controlMachineClient))
 		if err != nil {
@@ -296,7 +321,7 @@ func (dc *controller) enqueueMachineDeploymentAfter(deployment *v1alpha1.Machine
 }
 
 // getDeploymentForMachine returns the deployment managing the given Machine.
-func (dc *controller) getMachineDeploymentForMachine(ctx context.Context, machine *v1alpha1.Machine) *v1alpha1.MachineDeployment {
+func (dc *controller) getMachineDeploymentForMachine(machine *v1alpha1.Machine) *v1alpha1.MachineDeployment {
 	// Find the owning machine set
 	var is *v1alpha1.MachineSet
 	var err error
@@ -309,7 +334,8 @@ func (dc *controller) getMachineDeploymentForMachine(ctx context.Context, machin
 		// Not a Machine owned by a machine set.
 		return nil
 	}
-	is, err = dc.controlMachineClient.MachineSets(machine.Namespace).Get(ctx, controllerRef.Name, metav1.GetOptions{})
+
+	is, err = dc.machineSetLister.MachineSets(machine.Namespace).Get(controllerRef.Name)
 	if err != nil || is.UID != controllerRef.UID {
 		klog.V(4).Infof("Cannot get machineset %q for machine %q: %v", controllerRef.Name, machine.Name, err)
 		return nil
@@ -546,7 +572,10 @@ func (dc *controller) reconcileClusterMachineDeployment(key string) error {
 		return dc.rolloutRecreate(ctx, d, machineSets, machineMap)
 	case v1alpha1.RollingUpdateMachineDeploymentStrategyType:
 		return dc.rolloutRolling(ctx, d, machineSets, machineMap)
+	case v1alpha1.InPlaceUpdateMachineDeploymentStrategyType:
+		return dc.rolloutInPlace(ctx, d, machineSets, machineMap)
 	}
+
 	return fmt.Errorf("unexpected deployment strategy type: %s", d.Spec.Strategy.Type)
 }
 
diff --git a/pkg/controller/deployment_inplace.go b/pkg/controller/deployment_inplace.go
new file mode 100644
index 000000000..e6598ae6c
--- /dev/null
+++ b/pkg/controller/deployment_inplace.go
@@ -0,0 +1,619 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package controller
+
+import (
+	"context"
+	"fmt"
+	"maps"
+	"sort"
+
+	"github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
+	"github.com/gardener/machine-controller-manager/pkg/controller/autoscaler"
+	labelsutil "github.com/gardener/machine-controller-manager/pkg/util/labels"
+	"github.com/gardener/machine-controller-manager/pkg/util/nodeops"
+	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
+
+	v1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/klog/v2"
+	"k8s.io/utils/integer"
+)
+
+// rolloutInPlace implements the logic for rolling a machine set without replacing its machines.
+func (dc *controller) rolloutInPlace(ctx context.Context, d *v1alpha1.MachineDeployment, machineSetList []*v1alpha1.MachineSet, machineMap map[types.UID]*v1alpha1.MachineList) error {
+	clusterAutoscalerScaleDownAnnotations := make(map[string]string)
+	clusterAutoscalerScaleDownAnnotations[autoscaler.ClusterAutoscalerScaleDownDisabledAnnotationKey] = autoscaler.ClusterAutoscalerScaleDownDisabledAnnotationValue
+
+	// We do this to avoid accidentally deleting the user provided annotations.
+	clusterAutoscalerScaleDownAnnotations[autoscaler.ClusterAutoscalerScaleDownDisabledAnnotationByMCMKey] = autoscaler.ClusterAutoscalerScaleDownDisabledAnnotationByMCMValue
+
+	newMachineSet, oldMachineSets, err := dc.getAllMachineSetsAndSyncRevision(ctx, d, machineSetList, machineMap, true)
+	if err != nil {
+		return err
+	}
+	allMachineSets := append(oldMachineSets, newMachineSet)
+
+	if len(oldMachineSets) > 0 && !dc.machineSetsScaledToZero(oldMachineSets) {
+		// Label all the old machine sets to disable the scale up.
+		err := dc.labelMachineSets(ctx, oldMachineSets, map[string]string{machineutils.LabelKeyMachineSetScaleUpDisabled: "true"})
+		if err != nil {
+			klog.Errorf("failed to add label %s on all machine sets. Error: %v", machineutils.LabelKeyMachineSetScaleUpDisabled, err)
+			return err
+		}
+
+		// Add the annotation on the all machinesets if there are any old-machinesets and not scaled-to-zero.
+		// This also helps in annotating the node under new-machineset, incase the reconciliation is failing in next
+		// status-rollout steps.
+		if dc.autoscalerScaleDownAnnotationDuringRollout {
+			// Annotate all the nodes under this machine-deployment, as roll-out is on-going.
+			err := dc.annotateNodesBackingMachineSets(ctx, allMachineSets, clusterAutoscalerScaleDownAnnotations)
+			if err != nil {
+				klog.Errorf("failed to add annotations %s on all nodes. Error: %v", clusterAutoscalerScaleDownAnnotations, err)
+				return err
+			}
+		}
+	}
+
+	err = dc.taintNodesBackingMachineSets(
+		ctx,
+		oldMachineSets, &v1.Taint{
+			Key:    PreferNoScheduleKey,
+			Value:  "True",
+			Effect: "PreferNoSchedule",
+		},
+	)
+	if err != nil {
+		klog.Warningf("failed to add taint %s on all nodes. Error: %v", PreferNoScheduleKey, err)
+	}
+
+	// label all nodes backing old machine sets as candidate for update
+	if err := dc.labelNodesBackingMachineSets(ctx, oldMachineSets, v1alpha1.LabelKeyNodeCandidateForUpdate, "true"); err != nil {
+		return fmt.Errorf("failed to label nodes backing old machine sets as candidate for update: %v", err)
+	}
+
+	if err := dc.syncMachineSets(ctx, oldMachineSets, newMachineSet, d); err != nil {
+		return err
+	}
+
+	// In this section, we will attempt to scale up the new machine set. Machines with the `node.machine.sapcloud.io/update-successful` label
+	// can transfer their ownership to the new machine set.
+	// It is crucial to ensure that during the ownership transfer, the machine is not deleted,
+	// and the old machine set is not scaled up to recreate the machine.
+	scaledUp, err := dc.reconcileNewMachineSetInPlace(ctx, oldMachineSets, newMachineSet, d)
+	if err != nil {
+		klog.Errorf("failed to reconcile new machine set in place %s", err)
+		return err
+	}
+	if scaledUp {
+		// Update DeploymentStatus
+		return dc.syncRolloutStatus(ctx, allMachineSets, newMachineSet, d)
+	}
+
+	// prepare old machineSets for update
+	workDone, err := dc.reconcileOldMachineSetsInPlace(ctx, allMachineSets, FilterActiveMachineSets(oldMachineSets), newMachineSet, d)
+	if err != nil {
+		return err
+	}
+	if workDone {
+		// Update DeploymentStatus
+		return dc.syncRolloutStatus(ctx, allMachineSets, newMachineSet, d)
+	}
+
+	if MachineDeploymentComplete(d, &d.Status) {
+		if dc.autoscalerScaleDownAnnotationDuringRollout {
+			// Check if any of the machine under this MachineDeployment contains the by-mcm annotation, and
+			// remove the original autoscaler annotation only after.
+			err := dc.removeAutoscalerAnnotationsIfRequired(ctx, allMachineSets, clusterAutoscalerScaleDownAnnotations)
+			if err != nil {
+				return err
+			}
+		}
+		if err := dc.cleanupMachineDeployment(ctx, oldMachineSets, d); err != nil {
+			return err
+		}
+	}
+
+	// Sync deployment status
+	return dc.syncRolloutStatus(ctx, allMachineSets, newMachineSet, d)
+}
+
+// syncMachineSets syncs the machine sets by scaling up the new machine set and scaling down the old machine sets to the required replicas.
+func (dc *controller) syncMachineSets(ctx context.Context, oldMachineSets []*v1alpha1.MachineSet, newMachineSet *v1alpha1.MachineSet, deployment *v1alpha1.MachineDeployment) error {
+	newMachines, err := dc.machineLister.List(labels.SelectorFromSet(newMachineSet.Spec.Selector.MatchLabels))
+	if err != nil {
+		return err
+	}
+
+	machinesWithUpdateSuccessfulLabel := filterMachinesWithUpdateSuccessfulLabel(newMachines)
+	klog.V(3).Infof("Found %d machine(s) with label %q=%q in new machine set %q", len(machinesWithUpdateSuccessfulLabel), v1alpha1.LabelKeyNodeUpdateResult, v1alpha1.LabelValueNodeUpdateSuccessful, newMachineSet.Name)
+
+	if len(newMachines) > int(newMachineSet.Spec.Replicas) && len(machinesWithUpdateSuccessfulLabel) > 0 {
+		// scale up the new machine set to the number of machines with the update successful label.
+		// This is to ensure that the machines with moved to the new machine set when ownership is transferred is accounted.
+		scaleUpBy := min(len(machinesWithUpdateSuccessfulLabel), len(newMachines)-int(newMachineSet.Spec.Replicas))
+		klog.V(3).Infof("scale up the new machine set %s by %d to %d replicas", newMachineSet.Name, scaleUpBy, newMachineSet.Spec.Replicas+int32(scaleUpBy)) // #nosec G115 (CWE-190) -- value already validated
+		_, _, err := dc.scaleMachineSetAndRecordEvent(ctx, newMachineSet, newMachineSet.Spec.Replicas+int32(scaleUpBy), deployment)                          // #nosec G115 (CWE-190) -- value already validated
+		if err != nil {
+			return err
+		}
+	}
+
+	// remove labels from the machines related to the inplace update.
+	for _, machine := range machinesWithUpdateSuccessfulLabel {
+		labelsToRemove := []string{
+			v1alpha1.LabelKeyNodeUpdateResult,
+		}
+
+		patchBytes, err := labelsutil.RemoveLabels(labelsToRemove)
+		if err != nil {
+			return err
+		}
+
+		klog.V(3).Infof("removing label %v from machine %s", labelsToRemove, machine.Name)
+		if err := dc.machineControl.PatchMachine(ctx, machine.Namespace, machine.Name, patchBytes); err != nil {
+			klog.Errorf("error while removing label  %v : %v", labelsToRemove, err)
+			return err
+		}
+	}
+
+	// updates nodes associated with the machines to remove update-related labels and annotations, and uncordons them.
+	for _, newMachine := range newMachines {
+		nodeName, ok := newMachine.Labels[v1alpha1.NodeLabelKey]
+		if !ok {
+			return fmt.Errorf("node label not found for machine %s: %w", newMachine.Name, err)
+		}
+
+		node, err := dc.nodeLister.Get(nodeName)
+		if err != nil {
+			return fmt.Errorf("failed to get node %s: %w", nodeName, err)
+		}
+
+		cond := nodeops.GetCondition(node, v1alpha1.NodeInPlaceUpdate)
+		if isUpdateNotSuccessful(cond, node.Labels) {
+			continue
+		}
+
+		// remove labels related to the inplace update.
+		delete(node.Labels, v1alpha1.LabelKeyNodeCandidateForUpdate)
+		delete(node.Labels, v1alpha1.LabelKeyNodeSelectedForUpdate)
+		delete(node.Labels, v1alpha1.LabelKeyNodeUpdateResult)
+		// remove annotations related to the inplace update.
+		delete(node.Annotations, v1alpha1.AnnotationKeyMachineUpdateFailedReason)
+
+		// uncordon the node since the inplace update is successful.
+		node.Spec.Unschedulable = false
+
+		_, err = dc.targetCoreClient.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{})
+		if err != nil {
+			return fmt.Errorf("failed to remove inplace labels/annotations and uncordon node %s: %w", node.Name, err)
+		}
+	}
+
+	for _, oldMachineSet := range oldMachineSets {
+		// scale down the old machine set to the number of machines which is having the labelselector of the machine set.
+		oldMachines, err := dc.machineLister.List(labels.SelectorFromSet(oldMachineSet.Spec.Selector.MatchLabels))
+		if err != nil {
+			return fmt.Errorf("failed to list machines for machine set %s: %w", oldMachineSet.Name, err)
+		}
+
+		if len(oldMachines) < int(oldMachineSet.Spec.Replicas) {
+			_, _, err := dc.scaleMachineSetAndRecordEvent(ctx, oldMachineSet, int32(len(oldMachines)), deployment) // #nosec G115 (CWE-190) -- value already validated
+			if err != nil {
+				return fmt.Errorf("failed to scale down machine set %s: %w", oldMachineSet.Name, err)
+			}
+			klog.V(3).Infof("scaled down machine set %s to %d", oldMachineSet.Name, len(oldMachines))
+		}
+	}
+
+	return nil
+}
+
+func (dc *controller) reconcileNewMachineSetInPlace(ctx context.Context, oldMachineSets []*v1alpha1.MachineSet, newMachineSet *v1alpha1.MachineSet, deployment *v1alpha1.MachineDeployment) (bool, error) {
+	klog.V(3).Infof("reconcile new machine set %q having replicas %d", newMachineSet.Name, newMachineSet.Spec.Replicas)
+
+	if newMachineSet.Spec.Replicas == deployment.Spec.Replicas {
+		// Scaling not required.
+		return false, nil
+	}
+
+	if newMachineSet.Spec.Replicas > deployment.Spec.Replicas {
+		// Scale down.
+		scaled, _, err := dc.scaleMachineSetAndRecordEvent(ctx, newMachineSet, deployment.Spec.Replicas, deployment)
+		return scaled, err
+	}
+
+	oldMachinesCount := GetReplicaCountForMachineSets(oldMachineSets)
+	if oldMachinesCount == 0 {
+		scaled, _, err := dc.scaleMachineSetAndRecordEvent(ctx, newMachineSet, deployment.Spec.Replicas, deployment)
+		if err != nil {
+			return false, fmt.Errorf("failed to scale up machine set %s: %w", newMachineSet.Name, err)
+		}
+		klog.V(3).Infof("scaled up machine set %s to %d", newMachineSet.Name, deployment.Spec.Replicas)
+		return scaled, err
+	}
+
+	addedNewReplicasCount, err := dc.transferMachinesFromOldToNewMachineSet(ctx, oldMachineSets, newMachineSet, deployment)
+	if err != nil {
+		return false, fmt.Errorf("error while transferring machines from old to new machine set: %w", err)
+	}
+
+	if addedNewReplicasCount == 0 {
+		klog.V(3).Infof("no machines transferred to new machine set %s", newMachineSet.Name)
+		return false, nil
+	}
+
+	klog.V(3).Infof("scale up the new machine set %s by %d to %d replicas", newMachineSet.Name, addedNewReplicasCount, newMachineSet.Spec.Replicas+addedNewReplicasCount)
+	scaled, _, err := dc.scaleMachineSetAndRecordEvent(ctx, newMachineSet, newMachineSet.Spec.Replicas+addedNewReplicasCount, deployment)
+	return scaled, err
+}
+
+func (dc *controller) reconcileOldMachineSetsInPlace(ctx context.Context, allMachineSets []*v1alpha1.MachineSet, oldMachineSets []*v1alpha1.MachineSet, newMachineSet *v1alpha1.MachineSet, deployment *v1alpha1.MachineDeployment) (workDone bool, err error) {
+	oldMachinesCount := GetReplicaCountForMachineSets(oldMachineSets)
+	if oldMachinesCount == 0 {
+		// Can't scale down further
+		return false, nil
+	}
+
+	// If maxSurge is defined, there will be machines left in the old machine set that do not require an update
+	// because the new machine set already has the required replicas due to the additional machines added as per maxSurge.
+	// In that case we simply scale down the old machine set to zero.
+	if newMachineSet.Spec.Replicas == deployment.Spec.Replicas {
+		// Scale down old machine sets to zero.
+		for _, machineSet := range oldMachineSets {
+			_, _, err := dc.scaleMachineSetAndRecordEvent(ctx, machineSet, 0, deployment)
+			if err != nil {
+				return false, err
+			}
+		}
+		return true, nil
+	}
+
+	// Manual inplace update
+	if deployment.Spec.Strategy.InPlaceUpdate.OrchestrationType == v1alpha1.OrchestrationTypeManual {
+		return false, nil
+	}
+
+	allMachinesCount := GetReplicaCountForMachineSets(allMachineSets)
+	klog.V(3).Infof("New machine set %s has %d available machines.", newMachineSet.Name, newMachineSet.Status.AvailableReplicas)
+	maxUnavailable := MaxUnavailable(*deployment)
+
+	minAvailable := deployment.Spec.Replicas - maxUnavailable
+	newMachineSetUnavailableMachineCount := newMachineSet.Spec.Replicas - newMachineSet.Status.AvailableReplicas
+	oldMachineSetsMachinesUndergoingUpdate, err := dc.getMachinesUndergoingUpdate(oldMachineSets)
+	if err != nil {
+		return false, err
+	}
+
+	// Machines from old machine sets which are undergoing update will eventually move to new machine set.
+	// So once the current new machine set replcas + old machine set replicas undergoing update reaches the desired replicas,
+	// we can stop selecting machines from old machine sets for update.
+	if oldMachineSetsMachinesUndergoingUpdate+newMachineSet.Spec.Replicas >= deployment.Spec.Replicas {
+		return false, nil
+	}
+
+	klog.V(3).Infof("allMachinesCount:%d,  minAvailable:%d,  newMachineSetUnavailableMachineCount:%d,  oldISsMachineInUpdateProcess:%d", allMachinesCount, minAvailable, newMachineSetUnavailableMachineCount, oldMachineSetsMachinesUndergoingUpdate)
+
+	// maxUpdatePossible is calculated as the total number of machines (allMachinesCount)
+	// minus the minimum number of machines that must remain available (minAvailable),
+	// minus the number of machines in the new instance set that are currently unavailable (newMachineSetUnavailableMachineCount),
+	// minus the number of machines in the old instance sets that are undergoing updates (oldMachineSetsMachinesUndergoingUpdate).
+	// here unavailable machines of old machine sets are not considered as first we want to check if we can select machines for update from old machine sets
+	// after fulfilling all the constraints.
+	maxUpdatePossible := allMachinesCount - minAvailable - newMachineSetUnavailableMachineCount - oldMachineSetsMachinesUndergoingUpdate
+	if maxUpdatePossible <= 0 {
+		klog.V(3).Infof("no machines can be selected for update from old machine sets")
+		return false, nil
+	}
+
+	// prepare machines from old machine sets for update, need to check maxUnavailable to ensure we can select machines for update.
+	numOfMachinesSelectedForUpdate, err := dc.selectNumOfMachineForUpdate(ctx, allMachineSets, oldMachineSets, newMachineSet, deployment, oldMachineSetsMachinesUndergoingUpdate)
+	if err != nil {
+		return false, err
+	}
+
+	return numOfMachinesSelectedForUpdate > 0, nil
+}
+
+func (dc *controller) transferMachinesFromOldToNewMachineSet(ctx context.Context, oldMachineSets []*v1alpha1.MachineSet, newMachineSet *v1alpha1.MachineSet, deployment *v1alpha1.MachineDeployment) (int32, error) {
+	var addedNewReplicasCount int32
+
+	for _, oldMachineSet := range oldMachineSets {
+		transferredMachineCount := int32(0)
+		// get the machines for the machine set
+		oldMachines, err := dc.machineLister.List(labels.SelectorFromSet(oldMachineSet.Spec.Selector.MatchLabels))
+		if err != nil {
+			return addedNewReplicasCount, err
+		}
+
+		klog.V(3).Infof("Found %d machine(s) in old machine set %s", len(oldMachines), oldMachineSet.Name)
+
+		for _, oldMachine := range oldMachines {
+			nodeName, ok := oldMachine.Labels[v1alpha1.NodeLabelKey]
+			if !ok {
+				klog.Warningf("Node label not found for machine %s", oldMachine.Name)
+				continue
+			}
+
+			node, err := dc.nodeLister.Get(nodeName)
+			if err != nil {
+				if apierrors.IsNotFound(err) {
+					klog.Warningf("Node %s not found for machine %s", nodeName, oldMachine.Name)
+					continue
+				}
+				return addedNewReplicasCount, fmt.Errorf("failed to get node %s for machine %s: %w", nodeName, oldMachine.Name, err)
+			}
+
+			cond := getMachineCondition(oldMachine, v1alpha1.NodeInPlaceUpdate)
+			if isUpdateNotSuccessful(cond, node.Labels) {
+				continue
+			}
+
+			klog.V(3).Infof("Attempting to transfer machine %s to new machine set %s", oldMachine.Name, newMachineSet.Name)
+
+			labelsUniqueToOldMachine := removeLabelsNotCommingFromMachineSet(oldMachine.Labels, oldMachineSet.Spec.Selector.MatchLabels)
+			maps.Copy(labelsUniqueToOldMachine, newMachineSet.Spec.Selector.MatchLabels)
+			machineNewLabels := MergeStringMaps(labelsUniqueToOldMachine, map[string]string{v1alpha1.LabelKeyNodeUpdateResult: v1alpha1.LabelValueNodeUpdateSuccessful})
+
+			labelsJSONBytes, err := labelsutil.GetLabelsAsJSONBytes(machineNewLabels)
+			if err != nil {
+				return addedNewReplicasCount, err
+			}
+			// update the owner reference of the machine to the new machine set and update the labels
+			addControllerPatch := fmt.Sprintf(
+				`{"metadata":{"ownerReferences":[{"apiVersion":"machine.sapcloud.io/v1alpha1","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"labels":%s,"uid":"%s"}}`,
+				v1alpha1.SchemeGroupVersion.WithKind("MachineSet").Kind,
+				newMachineSet.GetName(), newMachineSet.GetUID(), string(labelsJSONBytes), oldMachine.UID)
+
+			err = dc.machineControl.PatchMachine(ctx, oldMachine.Namespace, oldMachine.Name, []byte(addControllerPatch))
+			if err != nil {
+				klog.Errorf("failed to transfer the ownership of machine %s to new machine set. Err: %v", oldMachine.Name, err)
+				return addedNewReplicasCount, err
+			}
+
+			// uncordon the node since the ownership of the machine has been transferred to the new machine set.
+			node.Spec.Unschedulable = false
+			_, err = dc.targetCoreClient.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{})
+			if err != nil {
+				return addedNewReplicasCount, fmt.Errorf("failed to uncordon the node %s: %w", node.Name, err)
+			}
+
+			transferredMachineCount++ // scale down the old machine set.
+			addedNewReplicasCount++   // scale up the new machine set.
+		}
+
+		if transferredMachineCount == 0 {
+			klog.V(3).Infof("no machines transferred from machine set %s", oldMachineSet.Name)
+			continue
+		}
+
+		klog.V(3).Infof("%d machine(s) transferred to new machine set. scaling down machine set %s to %d replicas", transferredMachineCount, oldMachineSet.Name, oldMachineSet.Spec.Replicas-transferredMachineCount)
+		_, _, err = dc.scaleMachineSetAndRecordEvent(ctx, oldMachineSet, oldMachineSet.Spec.Replicas-transferredMachineCount, deployment)
+		if err != nil {
+			klog.Errorf("scale down failed %s", err)
+			return addedNewReplicasCount, err
+		}
+	}
+
+	return addedNewReplicasCount, nil
+}
+
+func (dc *controller) selectNumOfMachineForUpdate(ctx context.Context, allMachineSets []*v1alpha1.MachineSet, oldMachineSets []*v1alpha1.MachineSet, newMachineSet *v1alpha1.MachineSet, deployment *v1alpha1.MachineDeployment, oldMachineSetsMachinesUndergoingUpdate int32) (int32, error) {
+	maxUnavailable := MaxUnavailable(*deployment)
+
+	// Check if we can pick machines from old ISes for updating to new IS.
+	minAvailable := deployment.Spec.Replicas - maxUnavailable
+
+	// Find the number of available machines.
+	availableMachineCount := GetAvailableReplicaCountForMachineSets(allMachineSets) - oldMachineSetsMachinesUndergoingUpdate
+	if availableMachineCount <= minAvailable {
+		// Cannot pick for updating.
+		return 0, nil
+	}
+
+	sort.Sort(MachineSetsByCreationTimestamp(oldMachineSets))
+
+	totalSelectedForUpdate := int32(0)
+	maxSelectableForUpdate := min(availableMachineCount-minAvailable, max(deployment.Spec.Replicas-newMachineSet.Spec.Replicas, 0))
+	for _, targetMachineSet := range oldMachineSets {
+		if totalSelectedForUpdate >= maxSelectableForUpdate {
+			// No further updating required.
+			break
+		}
+		if (targetMachineSet.Spec.Replicas) == 0 {
+			// cannot pick this ReplicaSet.
+			continue
+		}
+		// prepare for update
+		readyForUpdateCount := integer.Int32Min(targetMachineSet.Spec.Replicas, maxSelectableForUpdate-totalSelectedForUpdate) // #nosec G115 (CWE-190) -- value already validated
+		newReplicasCount := targetMachineSet.Spec.Replicas - readyForUpdateCount
+
+		if newReplicasCount > targetMachineSet.Spec.Replicas {
+			return 0, fmt.Errorf("when selecting machine from old IS for update, got invalid request %s %d -> %d", targetMachineSet.Name, targetMachineSet.Spec.Replicas, newReplicasCount)
+		}
+		selectedFromCurrentMachineSet, err := dc.labelMachinesToSelectedForUpdate(ctx, targetMachineSet, readyForUpdateCount)
+		if err != nil {
+			return totalSelectedForUpdate + selectedFromCurrentMachineSet, err
+		}
+
+		totalSelectedForUpdate += selectedFromCurrentMachineSet
+	}
+
+	return totalSelectedForUpdate, nil
+}
+
+// labelNodesBackingMachineSets labels all nodes belonging to the machineSets
+func (dc *controller) labelNodesBackingMachineSets(ctx context.Context, machineSets []*v1alpha1.MachineSet, labelKey, labelValue string) error {
+	for _, machineSet := range machineSets {
+
+		if machineSet == nil {
+			continue
+		}
+
+		klog.V(4).Infof("Attempting to label nodes belonging to MachineSet object %q with %v", machineSet.Name, labelKey)
+		filteredMachines, err := dc.machineLister.List(labels.SelectorFromSet(machineSet.Spec.Selector.MatchLabels))
+		if err != nil {
+			return err
+		}
+
+		for _, machine := range filteredMachines {
+			if err := dc.labelNodeForMachine(ctx, machine, labelKey, labelValue); err != nil {
+				return err
+			}
+		}
+
+		klog.V(3).Infof("Labeled nodes belonging to MachineSet %q with %v", machineSet.Name, labelKey)
+	}
+
+	return nil
+}
+
+func (dc *controller) labelNodeForMachine(ctx context.Context, machine *v1alpha1.Machine, labelKey, labelValue string) error {
+	if machine.Labels[v1alpha1.NodeLabelKey] == "" {
+		klog.V(3).Infof("Node label not found for machine %s", machine.Name)
+		return nil
+	}
+
+	node, err := dc.nodeLister.Get(machine.Labels[v1alpha1.NodeLabelKey])
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			return nil // Node is not found, continue to the next machine
+		}
+		klog.Errorf("Error occurred while trying to fetch node object: %v", err)
+		return err
+	}
+	if node.Labels[labelKey] == labelValue {
+		return nil
+	}
+
+	nodeCopy := node.DeepCopy()
+	nodeCopy.Labels = labelsutil.AddLabel(nodeCopy.Labels, labelKey, labelValue)
+	if _, err := dc.targetCoreClient.CoreV1().Nodes().Update(ctx, nodeCopy, metav1.UpdateOptions{}); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (dc *controller) labelMachinesToSelectedForUpdate(ctx context.Context, machineSet *v1alpha1.MachineSet, drainCount int32) (int32, error) {
+	numOfMachinesSelectedForUpdate := int32(0)
+
+	machines, err := dc.getMachinesForDrain(machineSet, drainCount)
+	if err != nil {
+		return numOfMachinesSelectedForUpdate, err
+	}
+
+	klog.V(3).Infof("machines selected for drain %v", machines)
+
+	for _, machine := range machines {
+		// labels on the node are added cumulatively and we can find both candidate-for-update and selected-for-update labels on the node.
+		if err := dc.labelNodeForMachine(ctx, machine, v1alpha1.LabelKeyNodeSelectedForUpdate, "true"); err != nil {
+			return numOfMachinesSelectedForUpdate, err
+		}
+		numOfMachinesSelectedForUpdate++
+	}
+
+	return numOfMachinesSelectedForUpdate, nil
+}
+
+func (dc *controller) getMachinesUndergoingUpdate(oldMachineSets []*v1alpha1.MachineSet) (int32, error) {
+	machineInUpdateProcess := int32(0)
+	for _, machineSet := range oldMachineSets {
+		machines, err := dc.machineLister.List(labels.SelectorFromSet(machineSet.Spec.Selector.MatchLabels))
+		if err != nil {
+			return 0, err
+		}
+
+		for _, machine := range machines {
+			if machine.Labels[v1alpha1.NodeLabelKey] == "" {
+				continue
+			}
+
+			node, err := dc.nodeLister.Get(machine.Labels[v1alpha1.NodeLabelKey])
+			if err != nil {
+				return machineInUpdateProcess, err
+			}
+
+			if _, ok := node.Labels[v1alpha1.LabelKeyNodeSelectedForUpdate]; ok {
+				machineInUpdateProcess++
+			}
+		}
+	}
+
+	return machineInUpdateProcess, nil
+}
+
+func (dc *controller) getMachinesForDrain(machineSet *v1alpha1.MachineSet, readyForDrain int32) ([]*v1alpha1.Machine, error) {
+	machines, err := dc.machineLister.List(labels.SelectorFromSet(machineSet.Spec.Selector.MatchLabels))
+	if err != nil {
+		return nil, err
+	}
+
+	var candidateForUpdateMachines []*v1alpha1.Machine
+	for _, machine := range machines {
+		if machine.Labels[v1alpha1.NodeLabelKey] == "" {
+			continue
+		}
+
+		node, err := dc.nodeLister.Get(machine.Labels[v1alpha1.NodeLabelKey])
+		if err != nil {
+			return candidateForUpdateMachines, err
+		}
+
+		if _, ok := node.Labels[v1alpha1.LabelKeyNodeCandidateForUpdate]; ok {
+			if _, ok := node.Labels[v1alpha1.LabelKeyNodeSelectedForUpdate]; !ok {
+				candidateForUpdateMachines = append(candidateForUpdateMachines, machine)
+			}
+			if len(candidateForUpdateMachines) == int(readyForDrain) {
+				return candidateForUpdateMachines, nil
+			}
+		}
+	}
+
+	return candidateForUpdateMachines, nil
+}
+
+// labelMachineSets label all the machineSets with the given label
+func (dc *controller) labelMachineSets(ctx context.Context, MachineSets []*v1alpha1.MachineSet, labels map[string]string) error {
+	for _, machineSet := range MachineSets {
+
+		if machineSet == nil {
+			continue
+		}
+
+		labels := MergeStringMaps(machineSet.Labels, labels)
+		formattedLabels, err := labelsutil.GetLabelsAsJSONBytes(labels)
+		if err != nil {
+			return err
+		}
+
+		addLabelPatch := fmt.Sprintf(`{"metadata":{"labels":%s}}`, string(formattedLabels))
+
+		if err := dc.machineSetControl.PatchMachineSet(ctx, machineSet.Namespace, machineSet.Name, []byte(addLabelPatch)); err != nil {
+			return fmt.Errorf("failed to label MachineSet %s: %w", machineSet.Name, err)
+		}
+	}
+
+	return nil
+}
+
+func isUpdateNotSuccessful(condition *v1.NodeCondition, labels map[string]string) bool {
+	return condition == nil || condition.Reason != v1alpha1.UpdateSuccessful || labels[v1alpha1.LabelKeyNodeUpdateResult] != v1alpha1.LabelValueNodeUpdateSuccessful
+}
+
+func removeLabelsNotCommingFromMachineSet(map1, map2 map[string]string) map[string]string {
+	out := make(map[string]string, len(map1))
+
+	maps.Copy(out, map1)
+
+	for k := range map2 {
+		delete(out, k)
+	}
+
+	return out
+}
diff --git a/pkg/controller/deployment_util.go b/pkg/controller/deployment_util.go
index 974f18a02..3dbce3ca1 100644
--- a/pkg/controller/deployment_util.go
+++ b/pkg/controller/deployment_util.go
@@ -25,18 +25,13 @@ package controller
 import (
 	"context"
 	"fmt"
-	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
+	"maps"
 	"reflect"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
 
-	"k8s.io/klog/v2"
-
-	"github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
-	v1alpha1client "github.com/gardener/machine-controller-manager/pkg/client/clientset/versioned/typed/machine/v1alpha1"
-	labelsutil "github.com/gardener/machine-controller-manager/pkg/util/labels"
 	v1 "k8s.io/api/core/v1"
 	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	"k8s.io/apimachinery/pkg/api/meta"
@@ -47,9 +42,15 @@ import (
 	"k8s.io/apimachinery/pkg/util/errors"
 	intstrutil "k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/apimachinery/pkg/util/wait"
+	"k8s.io/klog/v2"
 	"k8s.io/utils/integer"
+	"k8s.io/utils/ptr"
 
+	"github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
+	v1alpha1client "github.com/gardener/machine-controller-manager/pkg/client/clientset/versioned/typed/machine/v1alpha1"
 	v1alpha1listers "github.com/gardener/machine-controller-manager/pkg/client/listers/machine/v1alpha1"
+	labelsutil "github.com/gardener/machine-controller-manager/pkg/util/labels"
+	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
 )
 
 // MachineDeploymentListerExpansion allows custom methods to be added to MachineDeploymentLister.
@@ -619,15 +620,30 @@ func SetReplicasAnnotations(is *v1alpha1.MachineSet, desiredReplicas, maxReplica
 
 // MaxUnavailable returns the maximum unavailable machines a rolling deployment can take.
 func MaxUnavailable(deployment v1alpha1.MachineDeployment) int32 {
-	if !IsRollingUpdate(&deployment) || (deployment.Spec.Replicas) == 0 {
+	if !IsRollingUpdate(&deployment) && !IsInPlaceUpdate(&deployment) || (deployment.Spec.Replicas) == 0 {
 		return int32(0)
 	}
+
+	var maxSurge, maxUnavailable *intstrutil.IntOrString
+
+	if IsRollingUpdate(&deployment) {
+		maxSurge = deployment.Spec.Strategy.RollingUpdate.MaxSurge
+		maxUnavailable = deployment.Spec.Strategy.RollingUpdate.MaxUnavailable
+	} else {
+		maxSurge = deployment.Spec.Strategy.InPlaceUpdate.MaxSurge
+		maxUnavailable = deployment.Spec.Strategy.InPlaceUpdate.MaxUnavailable
+
+		if deployment.Spec.Strategy.InPlaceUpdate.OrchestrationType == v1alpha1.OrchestrationTypeManual {
+			maxSurge = ptr.To(intstrutil.FromInt(0))
+		}
+	}
+
 	// Error caught by validation
-	_, maxUnavailable, _ := ResolveFenceposts(deployment.Spec.Strategy.RollingUpdate.MaxSurge, deployment.Spec.Strategy.RollingUpdate.MaxUnavailable, (deployment.Spec.Replicas))
-	if maxUnavailable > deployment.Spec.Replicas {
+	_, maxUnavailableReplicas, _ := ResolveFenceposts(maxSurge, maxUnavailable, (deployment.Spec.Replicas))
+	if maxUnavailableReplicas > deployment.Spec.Replicas {
 		return deployment.Spec.Replicas
 	}
-	return maxUnavailable
+	return maxUnavailableReplicas
 }
 
 // MinAvailable returns the minimum available machines of a given deployment
@@ -732,6 +748,31 @@ func GetNewMachineSet(ctx context.Context, deployment *v1alpha1.MachineDeploymen
 	return FindNewMachineSet(deployment, rsList), nil
 }
 
+func filterMachinesWithUpdateSuccessfulLabel(machines []*v1alpha1.Machine) []*v1alpha1.Machine {
+	machinesWithUpdateSuccessfulLabel := make([]*v1alpha1.Machine, 0, len(machines))
+	for _, machine := range machines {
+		if labelValue, ok := machine.Labels[v1alpha1.LabelKeyNodeUpdateResult]; ok && labelValue == v1alpha1.LabelValueNodeUpdateSuccessful {
+			cond := getMachineCondition(machine, v1alpha1.NodeInPlaceUpdate)
+			// only consider machines with the update successful condition
+			if cond != nil && cond.Reason == v1alpha1.UpdateSuccessful {
+				machinesWithUpdateSuccessfulLabel = append(machinesWithUpdateSuccessfulLabel, machine)
+			}
+		}
+	}
+
+	return machinesWithUpdateSuccessfulLabel
+}
+
+// getMachineCondition returns a condition matching the type from the machines's status
+func getMachineCondition(machine *v1alpha1.Machine, conditionType v1.NodeConditionType) *v1.NodeCondition {
+	for _, cond := range machine.Status.Conditions {
+		if cond.Type == conditionType {
+			return &cond
+		}
+	}
+	return nil
+}
+
 // IsListFromClient returns an rsListFunc that wraps the given client.
 func IsListFromClient(ctx context.Context, c v1alpha1client.MachineV1alpha1Interface) IsListFunc {
 	return func(namespace string, options metav1.ListOptions) ([]*v1alpha1.MachineSet, error) {
@@ -1004,6 +1045,11 @@ func IsRollingUpdate(deployment *v1alpha1.MachineDeployment) bool {
 	return deployment.Spec.Strategy.Type == v1alpha1.RollingUpdateMachineDeploymentStrategyType
 }
 
+// IsInPlaceUpdate returns true if the strategy type is a inplace update.
+func IsInPlaceUpdate(deployment *v1alpha1.MachineDeployment) bool {
+	return deployment.Spec.Strategy.Type == v1alpha1.InPlaceUpdateMachineDeploymentStrategyType
+}
+
 // MachineDeploymentComplete considers a deployment to be complete once all of its desired replicas
 // are updated and available, and no old machines are running.
 func MachineDeploymentComplete(deployment *v1alpha1.MachineDeployment, newStatus *v1alpha1.MachineDeploymentStatus) bool {
@@ -1105,6 +1151,33 @@ func NewISNewReplicas(deployment *v1alpha1.MachineDeployment, allISs []*v1alpha1
 		return (newIS.Spec.Replicas) + scaleUpCount, nil
 	case v1alpha1.RecreateMachineDeploymentStrategyType:
 		return (deployment.Spec.Replicas), nil
+	case v1alpha1.InPlaceUpdateMachineDeploymentStrategyType:
+		if deployment.Spec.Strategy.InPlaceUpdate != nil && deployment.Spec.Strategy.InPlaceUpdate.OrchestrationType == v1alpha1.OrchestrationTypeManual {
+			// when newIs is created, its replicas should be zero as it will have machines only after the old machines are updated and
+			// moved to the new machine set
+			return 0, nil // In case of inplace update with manual update, newIS should not have any replicas
+		}
+
+		// For inplace update without manual update, when new IS is created we will scale it up to the max surge.
+		// Check if we can scale up.
+		maxSurge, err := intstrutil.GetValueFromIntOrPercent(deployment.Spec.Strategy.InPlaceUpdate.MaxSurge, int(deployment.Spec.Replicas), true)
+		if err != nil {
+			return 0, err
+		}
+		// Find the total number of machines
+		currentMachineCount := GetActualReplicaCountForMachineSets(allISs)
+		klog.V(3).Infof("current machine count accross all replicas %d", currentMachineCount)
+		maxTotalMachines := deployment.Spec.Replicas + int32(maxSurge) // #nosec G115 (CWE-190) -- value already validated
+		if currentMachineCount >= maxTotalMachines {
+			// Cannot scale up.
+			return newIS.Spec.Replicas, nil
+		}
+		// Scale up.
+		scaleUpCount := maxTotalMachines - currentMachineCount
+		// Do not exceed the number of desired replicas.
+		klog.V(3).Infof("new machine set %q replicas=%d", newIS.Name, newIS.Spec.Replicas+scaleUpCount)
+		scaleUpCount = integer.Int32Min(scaleUpCount, deployment.Spec.Replicas-newIS.Spec.Replicas) // #nosec G115 (CWE-190) -- Obtained from replicas and maxSurge, both of which are validated.
+		return newIS.Spec.Replicas + scaleUpCount, nil
 	default:
 		return 0, fmt.Errorf("machine deployment type %v isn't supported", deployment.Spec.Strategy.Type)
 	}
@@ -1220,3 +1293,24 @@ func statusUpdateRequired(old v1alpha1.MachineDeploymentStatus, new v1alpha1.Mac
 
 	return true
 }
+
+// MergeStringMaps merges the content of the newMaps with the oldMap. If a key already exists then
+// it gets overwritten by the last value with the same key.
+func MergeStringMaps[T any](oldMap map[string]T, newMaps ...map[string]T) map[string]T {
+	var out map[string]T
+
+	if oldMap != nil {
+		out = make(map[string]T, len(oldMap))
+	}
+	maps.Copy(out, oldMap)
+
+	for _, newMap := range newMaps {
+		if newMap != nil && out == nil {
+			out = make(map[string]T)
+		}
+
+		maps.Copy(out, newMap)
+	}
+
+	return out
+}
diff --git a/pkg/controller/machine_safety.go b/pkg/controller/machine_safety.go
index 0f17f367f..2ac152c22 100644
--- a/pkg/controller/machine_safety.go
+++ b/pkg/controller/machine_safety.go
@@ -234,8 +234,14 @@ func (c *controller) checkAndFreezeORUnfreezeMachineSets(ctx context.Context) er
 		if len(machineDeployments) >= 1 {
 			machineDeployment := machineDeployments[0]
 			if machineDeployment != nil {
+				var maxSurge *intstrutil.IntOrString
+				if machineDeployment.Spec.Strategy.RollingUpdate != nil {
+					maxSurge = machineDeployment.Spec.Strategy.RollingUpdate.MaxSurge
+				} else if machineDeployment.Spec.Strategy.InPlaceUpdate != nil {
+					maxSurge = machineDeployment.Spec.Strategy.InPlaceUpdate.MaxSurge
+				}
 				surge, err := intstrutil.GetValueFromIntOrPercent(
-					machineDeployment.Spec.Strategy.RollingUpdate.MaxSurge,
+					maxSurge,
 					int(machineDeployment.Spec.Replicas),
 					true,
 				)
diff --git a/pkg/controller/machineset.go b/pkg/controller/machineset.go
index 4cbe27fe0..05f9e569e 100644
--- a/pkg/controller/machineset.go
+++ b/pkg/controller/machineset.go
@@ -26,31 +26,29 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
 	"reflect"
 	"sort"
 	"sync"
 	"time"
 
-	v1alpha1client "github.com/gardener/machine-controller-manager/pkg/client/clientset/versioned/typed/machine/v1alpha1"
-	v1alpha1listers "github.com/gardener/machine-controller-manager/pkg/client/listers/machine/v1alpha1"
 	apiequality "k8s.io/apimachinery/pkg/api/equality"
-	errorsutil "k8s.io/apimachinery/pkg/util/errors"
-	"k8s.io/client-go/util/retry"
-
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
+	errorsutil "k8s.io/apimachinery/pkg/util/errors"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/client-go/tools/cache"
-	"k8s.io/utils/integer"
-
+	"k8s.io/client-go/util/retry"
 	"k8s.io/klog/v2"
+	"k8s.io/utils/integer"
 
 	"github.com/gardener/machine-controller-manager/pkg/apis/machine"
 	"github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
 	"github.com/gardener/machine-controller-manager/pkg/apis/machine/validation"
+	v1alpha1client "github.com/gardener/machine-controller-manager/pkg/client/clientset/versioned/typed/machine/v1alpha1"
+	v1alpha1listers "github.com/gardener/machine-controller-manager/pkg/client/listers/machine/v1alpha1"
+	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
 )
 
 const (
@@ -337,8 +335,15 @@ func (c *controller) manageReplicas(ctx context.Context, allMachines []*v1alpha1
 		return nil
 	}
 
-	var activeMachines, staleMachines []*v1alpha1.Machine
+	var activeMachines, staleMachines, machinesWithUpdateSuccessfulLabel []*v1alpha1.Machine
 	for _, m := range allMachines {
+		// Skip machines that are in the process of being updated.
+		if m.Labels[v1alpha1.LabelKeyNodeUpdateResult] == v1alpha1.LabelValueNodeUpdateSuccessful {
+			klog.V(3).Infof("Ignoring machine %s moved to new machine set during inplace update", m.Name)
+			machinesWithUpdateSuccessfulLabel = append(machinesWithUpdateSuccessfulLabel, m)
+			continue
+		}
+
 		if machineutils.IsMachineFailed(m) || machineutils.IsMachineTriggeredForDeletion(m) {
 			staleMachines = append(staleMachines, m)
 		} else if machineutils.IsMachineActive(m) {
@@ -355,6 +360,24 @@ func (c *controller) manageReplicas(ctx context.Context, allMachines []*v1alpha1
 	}
 
 	diff := len(activeMachines) - int(machineSet.Spec.Replicas)
+	// Removing the "update successful" label from a machine and scaling up the MachineSet are two separate operations.
+	// If the MachineSet is scaled up first and the "update successful" label is removed later, this controller might
+	// incorrectly assume that additional machines need to be created, as it ignores machines with the "update successful" label.
+	// Conversely, if the "update successful" label is removed first and the MachineSet is scaled up afterward, this controller
+	// might incorrectly assume that more machines need to be deleted, as machines without the label will be counted as active.
+	// To address this, we first check if the difference between the current active replicas and the desired replicas is negative.
+	// If it is, we then check if the difference plus the number of machines with the "update successful" label is greater than or equal to 0.
+	// In such cases, we set the difference to 0 because it is unclear whether the machine deployment controller still needs to increase
+	// the replicas of the MachineSet. Once the machine deployment controller removes the "update successful" label, the ambiguity will
+	// be resolved, and normal operations can proceed. This approach ensures that the controller does not create additional machines
+	// if the MachineSet is scaled up before the "update successful" label is removed.
+	if diff < 0 {
+		if diff+len(machinesWithUpdateSuccessfulLabel) >= 0 {
+			return nil
+		}
+		diff += len(machinesWithUpdateSuccessfulLabel)
+	}
+
 	klog.V(4).Infof("Difference between current active replicas and desired replicas - %d", diff)
 
 	if diff < 0 {
@@ -364,6 +387,13 @@ func (c *controller) manageReplicas(ctx context.Context, allMachines []*v1alpha1
 			return nil
 		}
 
+		// This case handles old MachineSets to prevent them from creating new machines when a machine is transferred to a new MachineSet,
+		// but the replica count has not been updated yet.
+		if _, ok := machineSet.Labels[machineutils.LabelKeyMachineSetScaleUpDisabled]; ok {
+			klog.V(2).Infof("MachineSet %s has label %s, and hence not creating new machines for it", machineSet.Name, machineutils.LabelKeyMachineSetScaleUpDisabled)
+			return nil
+		}
+
 		diff *= -1
 		if diff > BurstReplicas {
 			diff = BurstReplicas
diff --git a/pkg/controller/machineset_util.go b/pkg/controller/machineset_util.go
index 69d8ca6c4..78ce9d803 100644
--- a/pkg/controller/machineset_util.go
+++ b/pkg/controller/machineset_util.go
@@ -25,19 +25,18 @@ package controller
 import (
 	"context"
 	"fmt"
-	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
-
-	"k8s.io/klog/v2"
 
-	labelsutil "github.com/gardener/machine-controller-manager/pkg/util/labels"
 	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	errorsutil "k8s.io/apimachinery/pkg/util/errors"
 	"k8s.io/client-go/util/retry"
+	"k8s.io/klog/v2"
 
 	"github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
 	v1alpha1client "github.com/gardener/machine-controller-manager/pkg/client/clientset/versioned/typed/machine/v1alpha1"
 	v1alpha1listers "github.com/gardener/machine-controller-manager/pkg/client/listers/machine/v1alpha1"
+	labelsutil "github.com/gardener/machine-controller-manager/pkg/util/labels"
+	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
 )
 
 // TODO: use client library instead when it starts to support update retries
diff --git a/pkg/util/labels/labels.go b/pkg/util/labels/labels.go
index 5bb8a4334..7251b23c6 100644
--- a/pkg/util/labels/labels.go
+++ b/pkg/util/labels/labels.go
@@ -22,6 +22,9 @@ Modifications Copyright SAP SE or an SAP affiliate company and Gardener contribu
 package labels
 
 import (
+	"encoding/json"
+	"fmt"
+
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -127,3 +130,36 @@ func AddLabelToSelector(selector *metav1.LabelSelector, labelKey, labelValue str
 func SelectorHasLabel(selector *metav1.LabelSelector, labelKey string) bool {
 	return len(selector.MatchLabels[labelKey]) > 0
 }
+
+// GetLabelsAsJSONBytes retun labels in `json` format.
+func GetLabelsAsJSONBytes(labels map[string]string) ([]byte, error) {
+	patchBytes, err := json.Marshal(labels)
+	if err != nil {
+		return nil, fmt.Errorf("error marshaling patch data: %v", err)
+	}
+
+	return patchBytes, nil
+}
+
+// RemoveLabels returns a JSON patch to remove the given labels from an object.
+func RemoveLabels(labelsToRemove []string) ([]byte, error) {
+	// Create a map to represent the patch
+	patchMap := map[string]any{
+		"metadata": map[string]any{
+			"labels": map[string]any{},
+		},
+	}
+
+	// Set each label to null to remove it
+	for _, label := range labelsToRemove {
+		patchMap["metadata"].(map[string]any)["labels"].(map[string]any)[label] = nil
+	}
+
+	// Convert patchMap to JSON
+	patchBytes, err := json.Marshal(patchMap)
+	if err != nil {
+		return nil, fmt.Errorf("error marshaling patch data: %v", err)
+	}
+
+	return patchBytes, nil
+}
diff --git a/pkg/util/nodeops/conditions.go b/pkg/util/nodeops/conditions.go
index 23b97e915..b02257d73 100644
--- a/pkg/util/nodeops/conditions.go
+++ b/pkg/util/nodeops/conditions.go
@@ -46,8 +46,8 @@ func AddOrUpdateCondition(node *v1.Node, condition v1.NodeCondition) *v1.Node {
 	return newNode
 }
 
-// getNodeCondition returns a condition matching the type from the node's status
-func getNodeCondition(node *v1.Node, conditionType v1.NodeConditionType) *v1.NodeCondition {
+// GetCondition returns a condition matching the type from the node's status
+func GetCondition(node *v1.Node, conditionType v1.NodeConditionType) *v1.NodeCondition {
 	for _, cond := range node.Status.Conditions {
 		if cond.Type == conditionType {
 			return &cond
@@ -62,7 +62,7 @@ func GetNodeCondition(ctx context.Context, c clientset.Interface, nodeName strin
 	if err != nil {
 		return nil, err
 	}
-	return getNodeCondition(node, conditionType), nil
+	return GetCondition(node, conditionType), nil
 }
 
 // AddOrUpdateConditionsOnNode adds a condition to the node's status
diff --git a/pkg/util/provider/machinecontroller/controller.go b/pkg/util/provider/machinecontroller/controller.go
index baeabd5d3..f97286788 100644
--- a/pkg/util/provider/machinecontroller/controller.go
+++ b/pkg/util/provider/machinecontroller/controller.go
@@ -269,29 +269,29 @@ type controller struct {
 	podSynced               cache.InformerSynced
 }
 
-func (c *controller) Run(workers int, stopCh <-chan struct{}) {
+func (dc *controller) Run(workers int, stopCh <-chan struct{}) {
 
 	var (
 		waitGroup sync.WaitGroup
 	)
 
 	defer runtimeutil.HandleCrash()
-	defer c.permitGiver.Close()
-	defer c.nodeQueue.ShutDown()
-	defer c.secretQueue.ShutDown()
-	defer c.machineClassQueue.ShutDown()
-	defer c.machineQueue.ShutDown()
-	defer c.machineTerminationQueue.ShutDown()
-	defer c.machineSafetyOrphanVMsQueue.ShutDown()
-	defer c.machineSafetyAPIServerQueue.ShutDown()
-
-	if k8sutils.ConstraintK8sGreaterEqual121.Check(c.targetKubernetesVersion) {
-		if !cache.WaitForCacheSync(stopCh, c.secretSynced, c.pvcSynced, c.pvSynced, c.pdbSynced, c.volumeAttachementSynced, c.nodeSynced, c.machineClassSynced, c.machineSynced) {
+	defer dc.permitGiver.Close()
+	defer dc.nodeQueue.ShutDown()
+	defer dc.secretQueue.ShutDown()
+	defer dc.machineClassQueue.ShutDown()
+	defer dc.machineQueue.ShutDown()
+	defer dc.machineTerminationQueue.ShutDown()
+	defer dc.machineSafetyOrphanVMsQueue.ShutDown()
+	defer dc.machineSafetyAPIServerQueue.ShutDown()
+
+	if k8sutils.ConstraintK8sGreaterEqual121.Check(dc.targetKubernetesVersion) {
+		if !cache.WaitForCacheSync(stopCh, dc.secretSynced, dc.pvcSynced, dc.pvSynced, dc.pdbSynced, dc.volumeAttachementSynced, dc.nodeSynced, dc.machineClassSynced, dc.machineSynced) {
 			runtimeutil.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
 			return
 		}
 	} else {
-		if !cache.WaitForCacheSync(stopCh, c.secretSynced, c.pvcSynced, c.pvSynced, c.volumeAttachementSynced, c.nodeSynced, c.machineClassSynced, c.machineSynced) {
+		if !cache.WaitForCacheSync(stopCh, dc.secretSynced, dc.pvcSynced, dc.pvSynced, dc.volumeAttachementSynced, dc.nodeSynced, dc.machineClassSynced, dc.machineSynced) {
 			runtimeutil.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
 			return
 		}
@@ -303,16 +303,16 @@ func (c *controller) Run(workers int, stopCh <-chan struct{}) {
 	// be passed to the metrics registry. Collectors which added to the registry
 	// will collect metrics to expose them via the metrics endpoint of the mcm
 	// every time when the endpoint is called.
-	prometheus.MustRegister(c)
-
-	for i := 0; i < workers; i++ {
-		worker.Run(c.secretQueue, "ClusterSecret", worker.DefaultMaxRetries, true, c.reconcileClusterSecretKey, stopCh, &waitGroup)
-		worker.Run(c.machineClassQueue, "ClusterMachineClass", worker.DefaultMaxRetries, true, c.reconcileClusterMachineClassKey, stopCh, &waitGroup)
-		worker.Run(c.nodeQueue, "ClusterNode", worker.DefaultMaxRetries, true, c.reconcileClusterNodeKey, stopCh, &waitGroup)
-		worker.Run(c.machineQueue, "ClusterMachine", worker.DefaultMaxRetries, true, c.reconcileClusterMachineKey, stopCh, &waitGroup)
-		worker.Run(c.machineTerminationQueue, "ClusterMachineTermination", worker.DefaultMaxRetries, true, c.reconcileClusterMachineTermination, stopCh, &waitGroup)
-		worker.Run(c.machineSafetyOrphanVMsQueue, "ClusterMachineSafetyOrphanVMs", worker.DefaultMaxRetries, true, c.reconcileClusterMachineSafetyOrphanVMs, stopCh, &waitGroup)
-		worker.Run(c.machineSafetyAPIServerQueue, "ClusterMachineAPIServer", worker.DefaultMaxRetries, true, c.reconcileClusterMachineSafetyAPIServer, stopCh, &waitGroup)
+	prometheus.MustRegister(dc)
+
+	for range workers {
+		worker.Run(dc.secretQueue, "ClusterSecret", worker.DefaultMaxRetries, true, dc.reconcileClusterSecretKey, stopCh, &waitGroup)
+		worker.Run(dc.machineClassQueue, "ClusterMachineClass", worker.DefaultMaxRetries, true, dc.reconcileClusterMachineClassKey, stopCh, &waitGroup)
+		worker.Run(dc.nodeQueue, "ClusterNode", worker.DefaultMaxRetries, true, dc.reconcileClusterNodeKey, stopCh, &waitGroup)
+		worker.Run(dc.machineQueue, "ClusterMachine", worker.DefaultMaxRetries, true, dc.reconcileClusterMachineKey, stopCh, &waitGroup)
+		worker.Run(dc.machineTerminationQueue, "ClusterMachineTermination", worker.DefaultMaxRetries, true, dc.reconcileClusterMachineTermination, stopCh, &waitGroup)
+		worker.Run(dc.machineSafetyOrphanVMsQueue, "ClusterMachineSafetyOrphanVMs", worker.DefaultMaxRetries, true, dc.reconcileClusterMachineSafetyOrphanVMs, stopCh, &waitGroup)
+		worker.Run(dc.machineSafetyAPIServerQueue, "ClusterMachineAPIServer", worker.DefaultMaxRetries, true, dc.reconcileClusterMachineSafetyAPIServer, stopCh, &waitGroup)
 	}
 
 	<-stopCh
diff --git a/pkg/util/provider/machinecontroller/machine.go b/pkg/util/provider/machinecontroller/machine.go
index d2c73a877..fbb2e5ab5 100644
--- a/pkg/util/provider/machinecontroller/machine.go
+++ b/pkg/util/provider/machinecontroller/machine.go
@@ -222,7 +222,18 @@ func (c *controller) reconcileClusterMachine(ctx context.Context, machine *v1alp
 		if err != nil {
 			return retry, err
 		}
+
+		retry, err = c.updateNodeConditionBasedOnLabel(ctx, machine)
+		if err != nil {
+			return retry, err
+		}
+
+		retry, err = c.inPlaceUpdate(ctx, machine)
+		if err != nil {
+			return retry, err
+		}
 	}
+
 	if machine.Spec.ProviderID == "" || machine.Status.CurrentStatus.Phase == "" || machine.Status.CurrentStatus.Phase == v1alpha1.MachineCrashLoopBackOff {
 		return c.triggerCreationFlow(
 			ctx,
@@ -369,6 +380,11 @@ func (c *controller) updateNodeToMachine(oldObj, newObj interface{}) {
 		return
 	}
 
+	if inPlaceUpdateLabelsChanged(oldNode, node) {
+		c.enqueueMachine(machine, fmt.Sprintf("handling node UPDATE event. in-place update label added or updated for node %q", getNodeName(machine)))
+		return
+	}
+
 	c.addNodeToMachine(newObj)
 }
 
@@ -415,6 +431,28 @@ func (c *controller) getMachineFromNode(nodeName string) (*v1alpha1.Machine, err
 	return machines[0], nil
 }
 
+func inPlaceUpdateLabelsChanged(oldNode, node *corev1.Node) bool {
+	if oldNode == nil || node == nil {
+		return false
+	}
+
+	labelKeys := []string{
+		v1alpha1.LabelKeyNodeCandidateForUpdate,
+		v1alpha1.LabelKeyNodeSelectedForUpdate,
+		v1alpha1.LabelKeyNodeUpdateResult,
+	}
+
+	for _, key := range labelKeys {
+		oldVal, oldOk := oldNode.Labels[key]
+		newVal, newOk := node.Labels[key]
+		if (!oldOk && newOk) || (key == v1alpha1.LabelKeyNodeUpdateResult && oldVal != newVal) {
+			return true
+		}
+	}
+
+	return false
+}
+
 func addedOrRemovedEssentialTaints(oldNode, node *corev1.Node, taintKeys []string) bool {
 	mapOldNodeTaintKeys := make(map[string]bool)
 	mapNodeTaintKeys := make(map[string]bool)
diff --git a/pkg/util/provider/machinecontroller/machine_safety.go b/pkg/util/provider/machinecontroller/machine_safety.go
index dbae4a6ff..d189f9f9a 100644
--- a/pkg/util/provider/machinecontroller/machine_safety.go
+++ b/pkg/util/provider/machinecontroller/machine_safety.go
@@ -207,13 +207,13 @@ func (c *controller) AnnotateNodesUnmanagedByMCM(ctx context.Context) (machineut
 
 // checkCommonMachineClass checks for orphan VMs in MachinesClasses
 func (c *controller) checkMachineClasses(ctx context.Context) (machineutils.RetryPeriod, error) {
-	MachineClasses, err := c.machineClassLister.List(labels.Everything())
+	machineClasses, err := c.machineClassLister.List(labels.Everything())
 	if err != nil {
 		klog.Error("Safety-Net: Error getting machineClasses")
 		return machineutils.LongRetry, err
 	}
 
-	for _, machineClass := range MachineClasses {
+	for _, machineClass := range machineClasses {
 		retry, err := c.checkMachineClass(ctx, machineClass)
 		if err != nil {
 			return retry, err
diff --git a/pkg/util/provider/machinecontroller/machine_util.go b/pkg/util/provider/machinecontroller/machine_util.go
index ffd3ab411..f1f4606f0 100644
--- a/pkg/util/provider/machinecontroller/machine_util.go
+++ b/pkg/util/provider/machinecontroller/machine_util.go
@@ -56,6 +56,7 @@ import (
 	storageclient "k8s.io/client-go/kubernetes/typed/storage/v1"
 	storagelisters "k8s.io/client-go/listers/storage/v1"
 	"k8s.io/klog/v2"
+	"k8s.io/utils/ptr"
 )
 
 // emptyMap is a dummy emptyMap to compare with
@@ -204,7 +205,7 @@ func nodeConditionsHaveChanged(oldConditions []v1.NodeCondition, newConditions [
 	// checking for any added/updated new condition
 	for _, c := range newConditions {
 		oldC, exists := oldConditionsByType[c.Type]
-		if !exists || (oldC.Status != c.Status) {
+		if !exists || (oldC.Status != c.Status) || (c.Type == v1alpha1.NodeInPlaceUpdate && oldC.Reason != c.Reason) {
 			addedOrUpdatedConditions = append(addedOrUpdatedConditions, c)
 		}
 	}
@@ -265,6 +266,225 @@ func (c *controller) syncMachineNameToNode(ctx context.Context, machine *v1alpha
 	return machineutils.LongRetry, nil
 }
 
+func (c *controller) updateNodeConditionBasedOnLabel(ctx context.Context, machine *v1alpha1.Machine) (machineutils.RetryPeriod, error) {
+	node, err := c.nodeLister.Get(getNodeName(machine))
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			// Don't return error so that other steps can be executed.
+			return machineutils.LongRetry, nil
+		}
+		klog.Errorf("Error occurred while trying to fetch node object - err: %s", err)
+		return machineutils.ShortRetry, err
+	}
+
+	if !metav1.HasLabel(node.ObjectMeta, v1alpha1.LabelKeyNodeCandidateForUpdate) {
+		return machineutils.LongRetry, nil
+	}
+
+	nodeCopy := node.DeepCopy()
+
+	if nodeCopy.Labels == nil {
+		nodeCopy.Labels = make(map[string]string)
+	}
+
+	inPlaceCond := nodeops.GetCondition(nodeCopy, v1alpha1.NodeInPlaceUpdate)
+	updateCondition := false
+
+	// If the condition is not present, the in-place update is being initiated for the first time.
+	// In some cases, the node might already have the "selected for update" label, allowing the condition to be directly set to "selected for update."
+	// However, this approach could complicate handling, as it would require addressing scenarios where the node is marked as selected for update but has not yet been drained.
+	// If the condition is present and marked as update successful, we check if the node lacks the "update result" label.
+	// This ensures that the condition is not reverted from update successful to update candidate, as the node will already have the "update result" label from the same update process.
+	if inPlaceCond == nil || (inPlaceCond.Reason == v1alpha1.UpdateSuccessful && !metav1.HasLabel(nodeCopy.ObjectMeta, v1alpha1.LabelKeyNodeUpdateResult)) {
+		nodeCopy = nodeops.AddOrUpdateCondition(nodeCopy, v1.NodeCondition{
+			Type:               v1alpha1.NodeInPlaceUpdate,
+			Status:             v1.ConditionTrue,
+			LastTransitionTime: metav1.Now(),
+			Reason:             v1alpha1.CandidateForUpdate,
+			Message:            "Node is a candidate for in-place update",
+		})
+		updateCondition = true
+	}
+
+	if !updateCondition {
+		if _, ok := nodeCopy.Labels[v1alpha1.LabelKeyNodeSelectedForUpdate]; ok {
+			if inPlaceCond.Reason == v1alpha1.CandidateForUpdate {
+				nodeCopy = nodeops.AddOrUpdateCondition(nodeCopy, v1.NodeCondition{
+					Type:               v1alpha1.NodeInPlaceUpdate,
+					Status:             v1.ConditionTrue,
+					LastTransitionTime: metav1.Now(),
+					Reason:             v1alpha1.SelectedForUpdate,
+					Message:            "Node is selected for in-place update",
+				})
+				updateCondition = true
+			} else if inPlaceCond.Reason == v1alpha1.SelectedForUpdate {
+				// node still not has been drained
+				return machineutils.MediumRetry, nil
+			}
+		}
+	}
+
+	if !updateCondition {
+		if nodeCopy.Labels[v1alpha1.LabelKeyNodeUpdateResult] == v1alpha1.LabelValueNodeUpdateSuccessful {
+			if inPlaceCond != nil && inPlaceCond.Reason == v1alpha1.UpdateSuccessful {
+				return machineutils.LongRetry, nil
+			}
+			nodeCopy = nodeops.AddOrUpdateCondition(nodeCopy, v1.NodeCondition{
+				Type:               v1alpha1.NodeInPlaceUpdate,
+				Status:             v1.ConditionTrue,
+				LastTransitionTime: metav1.Now(),
+				Reason:             v1alpha1.UpdateSuccessful,
+				Message:            "Node in-place update successful",
+			})
+			updateCondition = true
+		} else if nodeCopy.Labels[v1alpha1.LabelKeyNodeUpdateResult] == v1alpha1.LabelValueNodeUpdateFailed {
+			if inPlaceCond != nil && inPlaceCond.Reason == v1alpha1.UpdateFailed {
+				return machineutils.LongRetry, nil
+			}
+
+			nodeCopy = nodeops.AddOrUpdateCondition(nodeCopy, v1.NodeCondition{
+				Type:               v1alpha1.NodeInPlaceUpdate,
+				Status:             v1.ConditionTrue,
+				LastTransitionTime: metav1.Now(),
+				Reason:             v1alpha1.UpdateFailed,
+				Message:            fmt.Sprintf("Node in-place update failed: %v", nodeCopy.Annotations[v1alpha1.AnnotationKeyMachineUpdateFailedReason]),
+			})
+			updateCondition = true
+		}
+	}
+
+	if updateCondition {
+		if _, err := c.targetCoreClient.CoreV1().Nodes().UpdateStatus(ctx, nodeCopy, metav1.UpdateOptions{}); err != nil {
+			if apierrors.IsConflict(err) {
+				return machineutils.ConflictRetry, err
+			}
+			return machineutils.ShortRetry, err
+		}
+	}
+
+	return machineutils.LongRetry, nil
+}
+
+func (c *controller) inPlaceUpdate(ctx context.Context, machine *v1alpha1.Machine) (machineutils.RetryPeriod, error) {
+	cond, err := nodeops.GetNodeCondition(ctx, c.targetCoreClient, getNodeName(machine), v1alpha1.NodeInPlaceUpdate)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			// Don't return error so that other steps can be executed.
+			return machineutils.LongRetry, nil
+		}
+		return machineutils.ShortRetry, err
+	}
+
+	if cond == nil {
+		return machineutils.LongRetry, nil
+	}
+
+	// if the condition is present and the reason is selected for update then drain the node
+	if cond.Reason == v1alpha1.SelectedForUpdate {
+		retry, err := c.drainNodeForInPlace(ctx, machine)
+		if err != nil {
+			return retry, err
+		}
+
+		// if the node is drained successfully then fetch the node condition again
+		cond, err = nodeops.GetNodeCondition(ctx, c.targetCoreClient, getNodeName(machine), v1alpha1.NodeInPlaceUpdate)
+		if err != nil {
+			return machineutils.ShortRetry, err
+		}
+	}
+
+	if cond.Reason == v1alpha1.ReadyForUpdate {
+		// give machine time for update to get applied
+		return machineutils.MediumRetry, fmt.Errorf("node %s is ready for in-place update", getNodeName(machine))
+	}
+
+	// if the condition is present and the reason is drain successful then the node is ready for update
+	if cond.Reason == v1alpha1.DrainSuccessful {
+		cond.Reason = v1alpha1.ReadyForUpdate
+		cond.LastTransitionTime = metav1.Now()
+		cond.Message = "Node is ready for in-place update"
+		if err := nodeops.AddOrUpdateConditionsOnNode(ctx, c.targetCoreClient, getNodeName(machine), *cond); err != nil {
+			return machineutils.ShortRetry, err
+		}
+		// give machine time for update to get applied
+		return machineutils.MediumRetry, fmt.Errorf("node %s is ready for in-place update", getNodeName(machine))
+	}
+
+	return machineutils.LongRetry, nil
+}
+
+func (c *controller) updateMachineStatusAndNodeCondition(ctx context.Context, machine *v1alpha1.Machine, description string, state v1alpha1.MachineState, drainError error) (machineutils.RetryPeriod, error) {
+	if drainError != nil {
+		updateRetryPeriod, updateErr := c.machineStatusUpdate(
+			ctx,
+			machine,
+			v1alpha1.LastOperation{
+				Description:    description,
+				State:          state,
+				Type:           v1alpha1.MachineOperationDrainNode,
+				LastUpdateTime: metav1.Now(),
+			},
+			// Let the clone.Status.CurrentStatus (LastUpdateTime) be as it was before.
+			// This helps while computing when the drain timeout to determine if force deletion is to be triggered.
+			// Ref - https://github.com/gardener/machine-controller-manager/blob/rel-v0.34.0/pkg/util/provider/machinecontroller/machine_util.go#L872
+			machine.Status.CurrentStatus,
+			machine.Status.LastKnownState,
+		)
+
+		if updateErr != nil {
+			return updateRetryPeriod, updateErr
+		}
+
+		return machineutils.ShortRetry, drainError
+	}
+
+	// update machine status to indicate that the machine will undergo an in-place update
+	description = fmt.Sprintf("Machine %s is undergoing an in-place update", machine.Name)
+	klog.V(2).Infof("%s with backing node %q is undergoing an in-place update", description, getNodeName(machine))
+
+	machine.Status.CurrentStatus = v1alpha1.CurrentStatus{
+		Phase:          v1alpha1.MachineInPlaceUpdating,
+		LastUpdateTime: metav1.Now(),
+	}
+	machine.Status.LastOperation = v1alpha1.LastOperation{
+		Description:    description,
+		State:          v1alpha1.MachineStateProcessing,
+		Type:           v1alpha1.MachineOperationInPlaceUpdate,
+		LastUpdateTime: metav1.Now(),
+	}
+
+	if _, err := c.controlMachineClient.Machines(machine.Namespace).UpdateStatus(ctx, machine, metav1.UpdateOptions{}); err != nil {
+		// Keep retrying across reconciles until update goes through
+		klog.Errorf("Update of Phase/Conditions failed for machine %q. Retrying, error: %q", machine.Name, err)
+		if apierrors.IsConflict(err) {
+			return machineutils.ConflictRetry, err
+		}
+	}
+
+	cond, err := nodeops.GetNodeCondition(ctx, c.targetCoreClient, getNodeName(machine), v1alpha1.NodeInPlaceUpdate)
+	if err != nil {
+		return machineutils.ShortRetry, err
+	}
+
+	if cond == nil {
+		// Add the condition to the node
+		cond = &v1.NodeCondition{
+			Type: v1alpha1.NodeInPlaceUpdate,
+		}
+	}
+
+	cond.Status = v1.ConditionTrue
+	cond.LastTransitionTime = metav1.Now()
+	cond.Reason = v1alpha1.DrainSuccessful
+	cond.Message = "Node draining successful"
+
+	if err := nodeops.AddOrUpdateConditionsOnNode(ctx, c.targetCoreClient, getNodeName(machine), *cond); err != nil {
+		return machineutils.ShortRetry, err
+	}
+
+	return machineutils.ShortRetry, err
+}
+
 // syncMachineNodeTemplate syncs nodeTemplates between machine and corresponding node-object.
 // It ensures, that any nodeTemplate element available on Machine should be available on node-object.
 // Although there could be more elements already available on node-object which will not be touched.
@@ -508,6 +728,9 @@ func (c *controller) machineCreateErrorHandler(ctx context.Context, machine *v1a
 	machineErr, ok := status.FromError(err)
 	if ok {
 		switch machineErr.Code() {
+		case codes.ResourceExhausted:
+			retryRequired = machineutils.LongRetry
+			lastKnownState = machine.Status.LastKnownState
 		case codes.Unknown, codes.DeadlineExceeded, codes.Aborted, codes.Unavailable:
 			retryRequired = machineutils.ShortRetry
 			lastKnownState = machine.Status.LastKnownState
@@ -663,56 +886,97 @@ func (c *controller) reconcileMachineHealth(ctx context.Context, machine *v1alph
 			cloneDirty = true
 		}
 
-		if c.isHealthy(clone) {
-			if clone.Status.CurrentStatus.Phase != v1alpha1.MachineRunning && !isPendingMachineWithCriticalComponentsNotReadyTaint(clone, node) {
-				if clone.Status.LastOperation.Type == v1alpha1.MachineOperationCreate &&
-					clone.Status.LastOperation.State != v1alpha1.MachineStateSuccessful {
-					// When machine creation went through
-					description = fmt.Sprintf("Machine %s successfully joined the cluster", clone.Name)
-					lastOperationType = v1alpha1.MachineOperationCreate
-
-					// Delete the bootstrap token
-					err = c.deleteBootstrapToken(ctx, clone.Name)
-					if err != nil {
-						klog.Warning(err)
+		// During the period when the machine is undergoing an in-place update or has failed to update,
+		// we cannot definitively determine if the machine is healthy.
+		// Because if the machine failed to update in-place, the severity of the failure is uncertain.
+		if machine.Status.CurrentStatus.Phase != v1alpha1.MachineInPlaceUpdating && machine.Status.CurrentStatus.Phase != v1alpha1.MachineInPlaceUpdateFailed {
+			if c.isHealthy(clone) {
+				if clone.Status.CurrentStatus.Phase != v1alpha1.MachineRunning && !isPendingMachineWithCriticalComponentsNotReadyTaint(clone, node) {
+					if clone.Status.LastOperation.Type == v1alpha1.MachineOperationCreate &&
+						clone.Status.LastOperation.State != v1alpha1.MachineStateSuccessful {
+						// When machine creation went through
+						description = fmt.Sprintf("Machine %s successfully joined the cluster", clone.Name)
+						lastOperationType = v1alpha1.MachineOperationCreate
+
+						// Delete the bootstrap token
+						err = c.deleteBootstrapToken(ctx, clone.Name)
+						if err != nil {
+							klog.Warning(err)
+						}
+					} else {
+						// Machine rejoined the cluster after a health-check
+						description = fmt.Sprintf("Machine %s successfully re-joined the cluster", clone.Name)
+						lastOperationType = v1alpha1.MachineOperationHealthCheck
+					}
+					klog.V(2).Infof("%s with backing node %q and providerID %q", description, getNodeName(clone), getProviderID(clone))
+
+					// Machine is ready and has joined/re-joined the cluster
+					clone.Status.LastOperation = v1alpha1.LastOperation{
+						Description:    description,
+						State:          v1alpha1.MachineStateSuccessful,
+						Type:           lastOperationType,
+						LastUpdateTime: metav1.Now(),
+					}
+					clone.Status.CurrentStatus = v1alpha1.CurrentStatus{
+						Phase: v1alpha1.MachineRunning,
+						// TimeoutActive:  false,
+						LastUpdateTime: metav1.Now(),
 					}
-				} else {
-					// Machine rejoined the cluster after a health-check
-					description = fmt.Sprintf("Machine %s successfully re-joined the cluster", clone.Name)
-					lastOperationType = v1alpha1.MachineOperationHealthCheck
+					cloneDirty = true
 				}
-				klog.V(2).Infof("%s with backing node %q and providerID %q", description, getNodeName(clone), getProviderID(clone))
+			} else {
+				if clone.Status.CurrentStatus.Phase == v1alpha1.MachineRunning {
+					// If machine is not healthy, and current phase is Running,
+					// change the machinePhase to Unknown and activate health check timeout
+					description = fmt.Sprintf("Machine %s is unhealthy - changing MachinePhase to Unknown. Node conditions: %+v", clone.Name, clone.Status.Conditions)
+					klog.Warning(description)
+
+					clone.Status.CurrentStatus = v1alpha1.CurrentStatus{
+						Phase: v1alpha1.MachineUnknown,
+						// TimeoutActive:  true,
+						LastUpdateTime: metav1.Now(),
+					}
+					clone.Status.LastOperation = v1alpha1.LastOperation{
+						Description:    description,
+						State:          v1alpha1.MachineStateProcessing,
+						Type:           v1alpha1.MachineOperationHealthCheck,
+						LastUpdateTime: metav1.Now(),
+					}
+					cloneDirty = true
+				}
+			}
+		}
+	}
 
-				// Machine is ready and has joined/re-joined the cluster
+	if !cloneDirty && (machine.Status.CurrentStatus.Phase == v1alpha1.MachineInPlaceUpdating ||
+		machine.Status.CurrentStatus.Phase == v1alpha1.MachineInPlaceUpdateFailed) {
+		// if the label update successful or failed, then skip the timeout check
+		if node != nil && metav1.HasLabel(node.ObjectMeta, v1alpha1.LabelKeyNodeUpdateResult) {
+			if node.Labels[v1alpha1.LabelKeyNodeUpdateResult] == v1alpha1.LabelValueNodeUpdateSuccessful && clone.Status.CurrentStatus.Phase != v1alpha1.MachineInPlaceUpdateSuccessful {
+				description = fmt.Sprintf("Machine %s successfully updated dependecies", machine.Name)
+				klog.V(2).Infof("%s with backing node %q and providerID %q sucessfully update the dependecies", description, getNodeName(machine), getProviderID(machine))
+				clone.Status.CurrentStatus = v1alpha1.CurrentStatus{
+					Phase:          v1alpha1.MachineInPlaceUpdateSuccessful,
+					LastUpdateTime: metav1.Now(),
+				}
 				clone.Status.LastOperation = v1alpha1.LastOperation{
 					Description:    description,
 					State:          v1alpha1.MachineStateSuccessful,
-					Type:           lastOperationType,
-					LastUpdateTime: metav1.Now(),
-				}
-				clone.Status.CurrentStatus = v1alpha1.CurrentStatus{
-					Phase: v1alpha1.MachineRunning,
-					// TimeoutActive:  false,
+					Type:           v1alpha1.MachineOperationInPlaceUpdate,
 					LastUpdateTime: metav1.Now(),
 				}
 				cloneDirty = true
-			}
-		} else {
-			if clone.Status.CurrentStatus.Phase == v1alpha1.MachineRunning {
-				// If machine is not healthy, and current phase is Running,
-				// change the machinePhase to Unknown and activate health check timeout
-				description = fmt.Sprintf("Machine %s is unhealthy - changing MachinePhase to Unknown. Node conditions: %+v", clone.Name, clone.Status.Conditions)
-				klog.Warning(description)
-
+			} else if node.Labels[v1alpha1.LabelKeyNodeUpdateResult] == v1alpha1.LabelValueNodeUpdateFailed && clone.Status.CurrentStatus.Phase != v1alpha1.MachineInPlaceUpdateFailed {
+				description = fmt.Sprintf("Machine %s failed to update dependecies: %s", machine.Name, node.Annotations[v1alpha1.AnnotationKeyMachineUpdateFailedReason])
+				klog.V(2).Infof("%s with backing node %q and providerID %q failed to update dependecies", description, getNodeName(machine), getProviderID(machine))
 				clone.Status.CurrentStatus = v1alpha1.CurrentStatus{
-					Phase: v1alpha1.MachineUnknown,
-					// TimeoutActive:  true,
+					Phase:          v1alpha1.MachineInPlaceUpdateFailed,
 					LastUpdateTime: metav1.Now(),
 				}
 				clone.Status.LastOperation = v1alpha1.LastOperation{
 					Description:    description,
-					State:          v1alpha1.MachineStateProcessing,
-					Type:           v1alpha1.MachineOperationHealthCheck,
+					State:          v1alpha1.MachineStateFailed,
+					Type:           v1alpha1.MachineOperationInPlaceUpdate,
 					LastUpdateTime: metav1.Now(),
 				}
 				cloneDirty = true
@@ -722,17 +986,22 @@ func (c *controller) reconcileMachineHealth(ctx context.Context, machine *v1alph
 
 	if !cloneDirty &&
 		(machine.Status.CurrentStatus.Phase == v1alpha1.MachinePending ||
-			machine.Status.CurrentStatus.Phase == v1alpha1.MachineUnknown) {
+			machine.Status.CurrentStatus.Phase == v1alpha1.MachineUnknown || machine.Status.CurrentStatus.Phase == v1alpha1.MachineInPlaceUpdating ||
+			machine.Status.CurrentStatus.Phase == v1alpha1.MachineInPlaceUpdateFailed) {
 		var (
 			description     string
 			timeOutDuration time.Duration
 		)
 
 		isMachinePending := machine.Status.CurrentStatus.Phase == v1alpha1.MachinePending
+		isMachineInPlaceUpdating := machine.Status.CurrentStatus.Phase == v1alpha1.MachineInPlaceUpdating
+		disableHealthTimeout := machine.Spec.MachineConfiguration != nil && ptr.Deref(machine.Spec.DisableHealthTimeout, false)
 		sleepTime := 1 * time.Minute
 
 		if isMachinePending {
 			timeOutDuration = c.getEffectiveCreationTimeout(machine).Duration
+		} else if isMachineInPlaceUpdating {
+			timeOutDuration = c.getEffectiveInPlaceUpdateTimeout(machine).Duration
 		} else {
 			timeOutDuration = c.getEffectiveHealthTimeout(machine).Duration
 		}
@@ -742,7 +1011,7 @@ func (c *controller) reconcileMachineHealth(ctx context.Context, machine *v1alph
 		if timeOut > 0 {
 			// Machine health timeout occurred while joining or rejoining of machine
 
-			if !isMachinePending {
+			if !isMachinePending && !isMachineInPlaceUpdating && !disableHealthTimeout {
 				// Timeout occurred due to machine being unhealthy for too long
 				description = fmt.Sprintf(
 					"Machine %s health checks failing since last %s minutes. Updating machine phase to Failed. Node Conditions: %+v",
@@ -756,27 +1025,49 @@ func (c *controller) reconcileMachineHealth(ctx context.Context, machine *v1alph
 				c.permitGiver.RegisterPermits(machineDeployName, 1)
 				return c.tryMarkingMachineFailed(ctx, machine, clone, machineDeployName, description, lockAcquireTimeout)
 			}
-			// Timeout occurred while machine creation
-			description = fmt.Sprintf(
-				"Machine %s failed to join the cluster in %s minutes.",
-				machine.Name,
-				timeOutDuration,
-			)
-			// Log the error message for machine failure
-			klog.Error(description)
 
-			clone.Status.LastOperation = v1alpha1.LastOperation{
-				Description:    description,
-				State:          v1alpha1.MachineStateFailed,
-				Type:           machine.Status.LastOperation.Type,
-				LastUpdateTime: metav1.Now(),
-			}
-			clone.Status.CurrentStatus = v1alpha1.CurrentStatus{
-				Phase: v1alpha1.MachineFailed,
-				// TimeoutActive:  false,
-				LastUpdateTime: metav1.Now(),
+			if isMachineInPlaceUpdating {
+				description = fmt.Sprintf(
+					"Machine %s failed to in-place update in %s minutes.",
+					machine.Name,
+					timeOutDuration,
+				)
+
+				klog.Error(description)
+
+				clone.Status.LastOperation = v1alpha1.LastOperation{
+					Description:    description,
+					State:          v1alpha1.MachineStateFailed,
+					Type:           v1alpha1.MachineOperationInPlaceUpdate,
+					LastUpdateTime: metav1.Now(),
+				}
+				clone.Status.CurrentStatus = v1alpha1.CurrentStatus{
+					Phase:          v1alpha1.MachineInPlaceUpdateFailed,
+					LastUpdateTime: metav1.Now(),
+				}
+				cloneDirty = true
+			} else if isMachinePending {
+				// Timeout occurred while machine creation
+				description = fmt.Sprintf(
+					"Machine %s failed to join the cluster in %s minutes.",
+					machine.Name,
+					timeOutDuration,
+				)
+				// Log the error message for machine failure
+				klog.Error(description)
+
+				clone.Status.LastOperation = v1alpha1.LastOperation{
+					Description:    description,
+					State:          v1alpha1.MachineStateFailed,
+					Type:           machine.Status.LastOperation.Type,
+					LastUpdateTime: metav1.Now(),
+				}
+				clone.Status.CurrentStatus = v1alpha1.CurrentStatus{
+					Phase:          v1alpha1.MachineFailed,
+					LastUpdateTime: metav1.Now(),
+				}
+				cloneDirty = true
 			}
-			cloneDirty = true
 		} else {
 			// If timeout has not occurred, re-enqueue the machine
 			// after a specified sleep time
@@ -1041,10 +1332,143 @@ func isConditionEmpty(condition v1.NodeCondition) bool {
 }
 
 // initializes err and description with the passed string message
-func printLogInitError(s string, err *error, description *string, machine *v1alpha1.Machine) {
+func printLogInitError(s string, err *error, description *string, machine *v1alpha1.Machine, isForInPlaceUpdate bool) {
 	klog.Warningf(s+" machine: %q ", machine.Name)
 	*err = fmt.Errorf(s+" %s", machineutils.InitiateVMDeletion)
 	*description = fmt.Sprintf(s+" %s", machineutils.InitiateVMDeletion)
+	if isForInPlaceUpdate {
+		*err = fmt.Errorf("%s", s)
+		*description = fmt.Sprint(s)
+	}
+}
+
+func (c *controller) drainNodeForInPlace(ctx context.Context, machine *v1alpha1.Machine) (machineutils.RetryPeriod, error) {
+	var (
+		// Declarations
+		node            *v1.Node
+		err             error
+		forceDeletePods bool
+		timeOutOccurred bool
+		description     string
+		state           v1alpha1.MachineState
+
+		readOnlyFileSystemCondition, nodeReadyCondition v1.NodeCondition
+
+		// Initialization
+		maxEvictRetries                             = int32(math.Min(float64(*c.getEffectiveMaxEvictRetries(machine)), c.getEffectiveDrainTimeout(machine).Seconds()/drain.PodEvictionRetryInterval.Seconds()))
+		pvDetachTimeOut                             = c.safetyOptions.PvDetachTimeout.Duration
+		pvReattachTimeOut                           = c.safetyOptions.PvReattachTimeout.Duration
+		timeOutDuration                             = c.getEffectiveDrainTimeout(machine).Duration
+		forceDrainLabelPresent                      = machine.Labels["force-drain"] == "True"
+		nodeName                                    = machine.Labels[v1alpha1.NodeLabelKey]
+		nodeNotReadyDuration                        = 5 * time.Minute
+		ReadonlyFilesystem     v1.NodeConditionType = "ReadonlyFilesystem"
+	)
+
+	for _, condition := range machine.Status.Conditions {
+		if condition.Type == v1.NodeReady {
+			nodeReadyCondition = condition
+		} else if condition.Type == ReadonlyFilesystem {
+			readOnlyFileSystemCondition = condition
+		}
+	}
+
+	// verify and log node object's existence
+	if node, err = c.nodeLister.Get(nodeName); err == nil {
+		klog.V(3).Infof("(drainNode) For node %q, machine %q, nodeReadyCondition: %s, readOnlyFileSystemCondition: %s", nodeName, machine.Name, nodeReadyCondition, readOnlyFileSystemCondition)
+	} else if apierrors.IsNotFound(err) {
+		klog.Warningf("(drainNode) Node %q for machine %q doesn't exist, so drain will finish instantly", nodeName, machine.Name)
+	}
+
+	if !isConditionEmpty(nodeReadyCondition) && (nodeReadyCondition.Status != v1.ConditionTrue) && (time.Since(nodeReadyCondition.LastTransitionTime.Time) > nodeNotReadyDuration) {
+		message := "Setting forceDeletePods to true for drain as machine is NotReady for over 5min"
+		forceDeletePods = true
+		printLogInitError(message, &err, &description, machine, true)
+	} else if !isConditionEmpty(readOnlyFileSystemCondition) && (readOnlyFileSystemCondition.Status != v1.ConditionFalse) && (time.Since(readOnlyFileSystemCondition.LastTransitionTime.Time) > nodeNotReadyDuration) {
+		message := "Setting forceDeletePods to true for drain as machine is in ReadonlyFilesystem for over 5min"
+		forceDeletePods = true
+		printLogInitError(message, &err, &description, machine, true)
+	}
+
+	if node != nil {
+		cond := nodeops.GetCondition(node, v1alpha1.NodeInPlaceUpdate)
+		if cond != nil && cond.Reason == v1alpha1.SelectedForUpdate {
+			timeOutOccurred = utiltime.HasTimeOutOccurred(cond.LastTransitionTime, timeOutDuration)
+		}
+	}
+
+	if forceDrainLabelPresent || timeOutOccurred {
+		forceDeletePods = true
+		timeOutDuration = 1 * time.Minute
+		maxEvictRetries = 1
+
+		klog.V(2).Infof(
+			"Force drain has been triggerred for machine %q with providerID %q and backing node %q due to Label:%t, timeout:%t",
+			machine.Name,
+			getProviderID(machine),
+			getNodeName(machine),
+			forceDrainLabelPresent,
+			timeOutOccurred,
+		)
+	} else {
+		klog.V(2).Infof(
+			"Normal drain has been triggerred for machine %q with providerID %q and backing node %q with drain-timeout:%v & maxEvictRetries:%d",
+			machine.Name,
+			getProviderID(machine),
+			getNodeName(machine),
+			timeOutDuration,
+			maxEvictRetries,
+		)
+	}
+
+	buf := bytes.NewBuffer([]byte{})
+	errBuf := bytes.NewBuffer([]byte{})
+
+	drainOptions := drain.NewDrainOptions(
+		c.targetCoreClient,
+		c.targetKubernetesVersion,
+		timeOutDuration,
+		maxEvictRetries,
+		pvDetachTimeOut,
+		pvReattachTimeOut,
+		nodeName,
+		-1,
+		forceDeletePods,
+		true,
+		true,
+		true,
+		buf,
+		errBuf,
+		c.driver,
+		c.pvcLister,
+		c.pvLister,
+		c.pdbLister,
+		c.nodeLister,
+		c.podLister,
+		c.volumeAttachmentHandler,
+		c.podSynced,
+	)
+
+	klog.V(3).Infof("(drainNode) Invoking RunDrain, forceDeletePods: %t, timeOutDuration: %s", forceDeletePods, timeOutDuration)
+	err = drainOptions.RunDrain(ctx)
+	if err == nil {
+		// Drain successful
+		klog.V(2).Infof("Drain successful for machine %q ,providerID %q, backing node %q. \nBuf:%v \nErrBuf:%v", machine.Name, getProviderID(machine), getNodeName(machine), buf, errBuf)
+
+		if forceDeletePods {
+			description = fmt.Sprintf("Force Drain successful. %s", machineutils.DelVolumesAttachments)
+		} else { // regular drain already waits for vol detach and attach for another node.
+			description = fmt.Sprintf("Drain successful. %s", machineutils.NodeReadyForUpdate)
+		}
+		state = v1alpha1.MachineStateProcessing
+	} else {
+		klog.Warningf("Drain failed for machine %q , providerID %q ,backing node %q. \nBuf:%v \nErrBuf:%v \nErr-Message:%v", machine.Name, getProviderID(machine), getNodeName(machine), buf, errBuf, err)
+
+		description = fmt.Sprintf("Drain failed due to - %s. Will retry in next sync. %s", err.Error(), machineutils.InitiateDrain)
+		state = v1alpha1.MachineStateProcessing
+	}
+
+	return c.updateMachineStatusAndNodeCondition(ctx, machine, description, state, err)
 }
 
 // drainNode attempts to drain the node backed by the machine object
@@ -1074,7 +1498,7 @@ func (c *controller) drainNode(ctx context.Context, deleteMachineRequest *driver
 
 	if nodeName == "" {
 		message := "Skipping drain as nodeName is not a valid one for machine."
-		printLogInitError(message, &err, &description, machine)
+		printLogInitError(message, &err, &description, machine, false)
 		skipDrain = true
 	} else {
 		for _, condition := range machine.Status.Conditions {
@@ -1096,12 +1520,12 @@ func (c *controller) drainNode(ctx context.Context, deleteMachineRequest *driver
 			message := "Setting forceDeletePods & forceDeleteMachine to true for drain as machine is NotReady for over 5min"
 			forceDeleteMachine = true
 			forceDeletePods = true
-			printLogInitError(message, &err, &description, machine)
+			printLogInitError(message, &err, &description, machine, false)
 		} else if !isConditionEmpty(readOnlyFileSystemCondition) && (readOnlyFileSystemCondition.Status != v1.ConditionFalse) && (time.Since(readOnlyFileSystemCondition.LastTransitionTime.Time) > nodeNotReadyDuration) {
 			message := "Setting forceDeletePods & forceDeleteMachine to true for drain as machine is in ReadonlyFilesystem for over 5min"
 			forceDeleteMachine = true
 			forceDeletePods = true
-			printLogInitError(message, &err, &description, machine)
+			printLogInitError(message, &err, &description, machine, false)
 		}
 	}
 
@@ -1472,6 +1896,16 @@ func (c *controller) getEffectiveCreationTimeout(machine *v1alpha1.Machine) *met
 	return effectiveCreationTimeout
 }
 
+func (c *controller) getEffectiveInPlaceUpdateTimeout(machine *v1alpha1.Machine) *metav1.Duration {
+	var effectiveDependenciesUpdateTimeout *metav1.Duration
+	if machine.Spec.MachineConfiguration != nil && machine.Spec.MachineConfiguration.MachineInPlaceUpdateTimeout != nil {
+		effectiveDependenciesUpdateTimeout = machine.Spec.MachineConfiguration.MachineInPlaceUpdateTimeout
+	} else {
+		effectiveDependenciesUpdateTimeout = &c.safetyOptions.MachineInPlaceUpdateTimeout
+	}
+	return effectiveDependenciesUpdateTimeout
+}
+
 // getEffectiveNodeConditions returns the nodeConditions set on the machine-object, otherwise returns the conditions set using the global-flag.
 func (c *controller) getEffectiveNodeConditions(machine *v1alpha1.Machine) *string {
 	var effectiveNodeConditions *string
diff --git a/pkg/util/provider/machineutils/utils.go b/pkg/util/provider/machineutils/utils.go
index 7d86d4176..dd9dae817 100644
--- a/pkg/util/provider/machineutils/utils.go
+++ b/pkg/util/provider/machineutils/utils.go
@@ -23,6 +23,9 @@ const (
 	// InitiateDrain specifies next step as initiate node drain
 	InitiateDrain = "Initiate node drain"
 
+	// NodeReadyForUpdate specifies next step as node ready for update.
+	NodeReadyForUpdate = "Node drain successful. Node is ready for update"
+
 	// DelVolumesAttachments specifies next step as deleting volume attachments
 	DelVolumesAttachments = "Delete Volume Attachments"
 
@@ -82,7 +85,7 @@ type RetryPeriod time.Duration
 const (
 	// ConflictRetry tells the controller to retry quickly - 200 milliseconds
 	ConflictRetry RetryPeriod = RetryPeriod(200 * time.Millisecond)
-	// ShortRetry tells the controller to retry after a short duration - 15 seconds
+	// ShortRetry tells the controller to retry after a short duration - 5 seconds
 	ShortRetry RetryPeriod = RetryPeriod(5 * time.Second)
 	// MediumRetry tells the controller to retry after a medium duration - 2 minutes
 	MediumRetry RetryPeriod = RetryPeriod(3 * time.Minute)



Test patch:
diff --git a/pkg/controller/controller_suite_test.go b/pkg/controller/controller_suite_test.go
index 87400cf14..ba8246ded 100644
--- a/pkg/controller/controller_suite_test.go
+++ b/pkg/controller/controller_suite_test.go
@@ -270,6 +270,11 @@ func newMachines(
 
 	currentTime := metav1.Now()
 
+	namePrefix := ""
+	if owner != nil && owner.Name != "" {
+		namePrefix = owner.Name + "-"
+	}
+
 	for i := range machines {
 		m := &v1alpha1.Machine{
 			TypeMeta: metav1.TypeMeta{
@@ -277,7 +282,7 @@ func newMachines(
 				Kind:       "Machine",
 			},
 			ObjectMeta: metav1.ObjectMeta{
-				Name:              fmt.Sprintf("machine-%d", i),
+				Name:              fmt.Sprintf("%smachine-%d", namePrefix, i),
 				Namespace:         testNamespace,
 				Labels:            labels,
 				Annotations:       annotations,
@@ -302,18 +307,19 @@ func newMachines(
 
 func newNode(
 	_ int,
+	labels map[string]string,
 	nodeSpec *corev1.NodeSpec,
 	nodeStatus *corev1.NodeStatus,
 ) *corev1.Node {
-	return newNodes(1, nodeSpec, nodeStatus)[0]
+	return newNodes(1, labels, nodeSpec, nodeStatus)[0]
 }
 
 func newNodes(
 	nodeCount int,
+	labels map[string]string,
 	nodeSpec *corev1.NodeSpec,
 	_ *corev1.NodeStatus,
 ) []*corev1.Node {
-
 	nodes := make([]*corev1.Node, nodeCount)
 	for i := range nodes {
 		node := &corev1.Node{
@@ -322,7 +328,8 @@ func newNodes(
 				Kind:       "Node",
 			},
 			ObjectMeta: metav1.ObjectMeta{
-				Name: fmt.Sprintf("node-%d", i),
+				Name:   fmt.Sprintf("node-%d", i),
+				Labels: labels,
 			},
 			Spec: *nodeSpec.DeepCopy(),
 		}
@@ -447,7 +454,7 @@ func createController(
 		machineDeploymentQueue:         workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "machinedeployment"),
 		machineSafetyOvershootingQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "machinesafetyovershooting"),
 		expectations:                   NewUIDTrackingContExpectations(NewContExpectations()),
-		recorder:                       record.NewBroadcaster().NewRecorder(nil, corev1.EventSource{Component: ""}),
+		recorder:                       record.NewBroadcaster().NewRecorder(internalExternalScheme, corev1.EventSource{Component: ""}),
 	}
 
 	// controller.internalExternalScheme = runtime.NewScheme()
diff --git a/pkg/controller/controller_utils_test.go b/pkg/controller/controller_utils_test.go
index e498dd0b1..83e40a656 100644
--- a/pkg/controller/controller_utils_test.go
+++ b/pkg/controller/controller_utils_test.go
@@ -286,6 +286,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
@@ -311,6 +312,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
@@ -333,6 +335,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
@@ -357,6 +360,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
@@ -381,6 +385,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
@@ -461,6 +466,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
@@ -486,6 +492,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
@@ -506,6 +513,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
@@ -531,6 +539,7 @@ var _ = Describe("#controllerUtils", func() {
 				setup: setup{
 					node: newNode(
 						1,
+						nil,
 						&corev1.NodeSpec{},
 						nil,
 					),
diff --git a/pkg/controller/deployment_inplace_test.go b/pkg/controller/deployment_inplace_test.go
new file mode 100644
index 000000000..b837f9fbb
--- /dev/null
+++ b/pkg/controller/deployment_inplace_test.go
@@ -0,0 +1,1305 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package controller
+
+import (
+	"context"
+	"fmt"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/util/intstr"
+	"k8s.io/utils/ptr"
+
+	machinev1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
+)
+
+var _ = Describe("deployment_inplace", func() {
+	Describe("syncMachineSets", func() {
+		type setup struct {
+			oldMachineSetReplicas                  int32
+			oldMSMachinesMovedToNewMS              int32
+			newMachineSetReplicas                  int32
+			newMSMachinesWithUpdateSuccessfulLabel int32
+		}
+		type expect struct {
+			oldMachineSetReplicas int32
+			newMachineSetReplicas int32
+		}
+		type data struct {
+			setup  setup
+			expect expect
+		}
+
+		machineSets := newMachineSets(
+			2,
+			&machinev1.MachineTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "machineset-0",
+				},
+				Spec: machinev1.MachineSpec{
+					Class: machinev1.ClassSpec{
+						Kind: "MachineClass",
+						Name: "test-machine-class",
+					},
+				},
+			}, 1, 500, &machinev1.MachineSetStatus{AvailableReplicas: 1}, nil, nil, map[string]string{"machineset": "old"})
+
+		oldMachineSet := machineSets[0]
+		newMachineSet := machineSets[1]
+		oldMachineSet.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"machineset": "old"}}
+		newMachineSet.Labels = map[string]string{"machineset": "new"}
+		newMachineSet.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"machineset": "new"}}
+
+		deployment := &machinev1.MachineDeployment{
+			Spec: machinev1.MachineDeploymentSpec{
+				Replicas: int32(3),
+				Strategy: machinev1.MachineDeploymentStrategy{
+					Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+					InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+						UpdateConfiguration: machinev1.UpdateConfiguration{
+							MaxUnavailable: ptr.To(intstr.FromInt32(1)),
+							MaxSurge:       ptr.To(intstr.FromInt32(0)),
+						},
+					},
+				},
+			},
+		}
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				oldMachineSet.Spec.Replicas = data.setup.oldMachineSetReplicas
+				newMachineSet.Spec.Replicas = data.setup.newMachineSetReplicas
+
+				controlMachineObjects := []runtime.Object{}
+				controlMachineObjects = append(controlMachineObjects, oldMachineSet, newMachineSet)
+
+				machines := []*machinev1.Machine{}
+				machines = append(machines, newMachinesFromMachineSet(int(data.setup.oldMachineSetReplicas-data.setup.oldMSMachinesMovedToNewMS), oldMachineSet, &machinev1.MachineStatus{}, nil, map[string]string{"machineset": "old"})...)
+
+				newMachines := newMachinesFromMachineSet(int(data.setup.newMachineSetReplicas+data.setup.newMSMachinesWithUpdateSuccessfulLabel), newMachineSet, &machinev1.MachineStatus{}, nil, map[string]string{})
+				machinesWithUpdateSuccessful := 0
+				for i := range newMachines {
+					newMachines[i].Labels = map[string]string{
+						"machineset":           "new",
+						machinev1.NodeLabelKey: fmt.Sprintf("node-%d", i),
+					}
+
+					if machinesWithUpdateSuccessful < int(data.setup.newMSMachinesWithUpdateSuccessfulLabel) {
+						newMachines[i].Labels[machinev1.LabelKeyNodeUpdateResult] = machinev1.LabelValueNodeUpdateSuccessful
+
+						newMachines[i].Status.Conditions = []corev1.NodeCondition{
+							{
+								Type:   machinev1.NodeInPlaceUpdate,
+								Reason: machinev1.UpdateSuccessful,
+							},
+						}
+
+						machinesWithUpdateSuccessful++
+					}
+				}
+
+				machines = append(machines, newMachines...)
+				for _, o := range machines {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+
+				nodes := newNodes(int(data.setup.newMachineSetReplicas+data.setup.newMSMachinesWithUpdateSuccessfulLabel), map[string]string{}, &corev1.NodeSpec{}, nil)
+				nodesWithUpdateSuccessful := 0
+				for i := range nodes {
+					if nodesWithUpdateSuccessful < int(data.setup.newMSMachinesWithUpdateSuccessfulLabel) {
+						nodes[i].Labels = map[string]string{
+							machinev1.LabelKeyNodeCandidateForUpdate: "true",
+							machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+							machinev1.LabelKeyNodeUpdateResult:       machinev1.LabelValueNodeUpdateSuccessful,
+						}
+						nodes[i].Spec.Unschedulable = true
+						nodesWithUpdateSuccessful++
+
+						nodes[i].Status.Conditions = []corev1.NodeCondition{
+							{
+								Type:   machinev1.NodeInPlaceUpdate,
+								Reason: machinev1.UpdateSuccessful,
+							},
+						}
+					}
+				}
+
+				targetCoreObjects := []runtime.Object{}
+				for _, o := range nodes {
+					targetCoreObjects = append(targetCoreObjects, o)
+				}
+
+				controller, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects)
+				defer trackers.Stop()
+				waitForCacheSync(stop, controller)
+
+				err := controller.syncMachineSets(context.TODO(), []*machinev1.MachineSet{oldMachineSet}, newMachineSet, deployment)
+				Expect(err).ToNot(HaveOccurred())
+
+				actualOldMachineSet, err := controller.controlMachineClient.MachineSets(testNamespace).Get(context.TODO(), oldMachineSet.Name, metav1.GetOptions{})
+				Expect(err).ToNot(HaveOccurred())
+				Expect(actualOldMachineSet.Spec.Replicas).To(Equal(data.expect.oldMachineSetReplicas))
+
+				actualNewMachineSet, err := controller.controlMachineClient.MachineSets(testNamespace).Get(context.TODO(), newMachineSet.Name, metav1.GetOptions{})
+				Expect(err).ToNot(HaveOccurred())
+				Expect(actualNewMachineSet.Spec.Replicas).To(Equal(data.expect.newMachineSetReplicas))
+
+				for _, expectedMachine := range newMachines {
+					actualMachine, err := controller.controlMachineClient.Machines(testNamespace).Get(context.TODO(), expectedMachine.Name, metav1.GetOptions{})
+					Expect(err).ToNot(HaveOccurred())
+					Expect(actualMachine.Labels).ToNot(HaveKey(machinev1.LabelKeyNodeUpdateResult))
+				}
+
+				actualNodes, err := controller.targetCoreClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
+				Expect(err).ToNot(HaveOccurred())
+				for i := range actualNodes.Items {
+					node := actualNodes.Items[i]
+					Expect(node.Spec.Unschedulable).To(Equal(false))
+					Expect(node.Labels).ToNot(HaveKey(machinev1.LabelKeyNodeCandidateForUpdate))
+					Expect(node.Labels).ToNot(HaveKey(machinev1.LabelKeyNodeSelectedForUpdate))
+					Expect(node.Labels).ToNot(HaveKey(machinev1.LabelKeyNodeUpdateResult))
+				}
+			},
+
+			Entry("no scaling required as there are no extra machines affiliated to new machine set with update successful label", &data{
+				setup: setup{
+					oldMachineSetReplicas:                  2,
+					oldMSMachinesMovedToNewMS:              0,
+					newMachineSetReplicas:                  3,
+					newMSMachinesWithUpdateSuccessfulLabel: 0,
+				},
+				expect: expect{
+					oldMachineSetReplicas: 2,
+					newMachineSetReplicas: 3,
+				},
+			}),
+			Entry("scale up new machine set because there are machines with update successful condition", &data{
+				setup: setup{
+					oldMachineSetReplicas:                  2,
+					oldMSMachinesMovedToNewMS:              0,
+					newMachineSetReplicas:                  2,
+					newMSMachinesWithUpdateSuccessfulLabel: 1,
+				},
+				expect: expect{
+					oldMachineSetReplicas: 2,
+					newMachineSetReplicas: 3,
+				},
+			}),
+			Entry("scale down old machine set because there are less machines than the replicas count", &data{
+				setup: setup{
+					oldMachineSetReplicas:                  2,
+					oldMSMachinesMovedToNewMS:              1,
+					newMachineSetReplicas:                  3,
+					newMSMachinesWithUpdateSuccessfulLabel: 0,
+				},
+				expect: expect{
+					oldMachineSetReplicas: 1,
+					newMachineSetReplicas: 3,
+				},
+			}),
+		)
+	})
+
+	Describe("reconcileNewMachineSetInPlace", func() {
+		type setup struct {
+			oldMachineSetReplicas     int32
+			newMachineSetReplicas     int32
+			nodesWithUpdateSuccessful int
+		}
+		type expect struct {
+			scaled bool
+		}
+		type data struct {
+			setup  setup
+			expect expect
+		}
+
+		machineSets := newMachineSets(
+			2,
+			&machinev1.MachineTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "machineset-0",
+				},
+				Spec: machinev1.MachineSpec{
+					Class: machinev1.ClassSpec{
+						Kind: "MachineClass",
+						Name: "test-machine-class",
+					},
+				},
+			}, 1, 500, &machinev1.MachineSetStatus{AvailableReplicas: 1}, nil, nil, map[string]string{"key": "value"})
+
+		oldMachineSet := machineSets[0]
+		newMachineSet := machineSets[1]
+
+		deployment := &machinev1.MachineDeployment{
+			Spec: machinev1.MachineDeploymentSpec{
+				Replicas: int32(3),
+				Strategy: machinev1.MachineDeploymentStrategy{
+					Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+					InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+						UpdateConfiguration: machinev1.UpdateConfiguration{
+							MaxUnavailable: ptr.To(intstr.FromInt32(1)),
+							MaxSurge:       ptr.To(intstr.FromInt32(0)),
+						},
+					},
+				},
+			},
+		}
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				oldMachineSet.Spec.Replicas = data.setup.oldMachineSetReplicas
+				newMachineSet.Spec.Replicas = data.setup.newMachineSetReplicas
+
+				controlMachineObjects := []runtime.Object{}
+				controlMachineObjects = append(controlMachineObjects, oldMachineSet, newMachineSet)
+
+				machines := []*machinev1.Machine{}
+				machines = append(machines, newMachinesFromMachineSet(int(data.setup.oldMachineSetReplicas), oldMachineSet, &machinev1.MachineStatus{}, nil, map[string]string{"key": "value"})...)
+				machines = append(machines, newMachinesFromMachineSet(int(data.setup.newMachineSetReplicas), newMachineSet, &machinev1.MachineStatus{}, nil, nil)...)
+				machinesWithUpdateSuccessful := 0
+				for i := range machines {
+					machines[i].Labels = map[string]string{
+						machinev1.NodeLabelKey: fmt.Sprintf("node-%d", i),
+					}
+					if machinesWithUpdateSuccessful < data.setup.nodesWithUpdateSuccessful {
+						machines[i].Status.Conditions = []corev1.NodeCondition{
+							{
+								Type:   machinev1.NodeInPlaceUpdate,
+								Reason: machinev1.UpdateSuccessful,
+							},
+						}
+						machinesWithUpdateSuccessful++
+					}
+				}
+
+				for _, o := range machines {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+
+				nodes := newNodes(int(data.setup.oldMachineSetReplicas), map[string]string{}, &corev1.NodeSpec{}, nil)
+				nodesWithUpdateSuccessful := 0
+				for i := range nodes {
+					if nodesWithUpdateSuccessful < data.setup.nodesWithUpdateSuccessful {
+						nodes[i].Labels = map[string]string{machinev1.LabelKeyNodeUpdateResult: machinev1.LabelValueNodeUpdateSuccessful}
+						nodes[i].Status.Conditions = []corev1.NodeCondition{
+							{
+								Type:   machinev1.NodeInPlaceUpdate,
+								Reason: machinev1.UpdateSuccessful,
+							},
+						}
+						nodesWithUpdateSuccessful++
+					}
+				}
+
+				targetCoreObjects := []runtime.Object{}
+				for _, o := range nodes {
+					targetCoreObjects = append(targetCoreObjects, o)
+				}
+
+				controller, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects)
+				defer trackers.Stop()
+				waitForCacheSync(stop, controller)
+
+				scaled, err := controller.reconcileNewMachineSetInPlace(context.TODO(), []*machinev1.MachineSet{oldMachineSet}, newMachineSet, deployment)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(scaled).To(Equal(data.expect.scaled))
+
+				machinesWithUpdateSuccessful = 0
+				for i := range machines {
+					if machinesWithUpdateSuccessful < data.setup.nodesWithUpdateSuccessful {
+						actualMachine, err := controller.controlMachineClient.Machines(testNamespace).Get(context.TODO(), machines[i].Name, metav1.GetOptions{})
+						Expect(err).ToNot(HaveOccurred())
+						Expect(actualMachine.Labels).To(HaveKey(machinev1.LabelKeyNodeUpdateResult))
+						machinesWithUpdateSuccessful++
+					}
+				}
+			},
+
+			Entry("no scaling required as newMachineSet replicas match deployment replicas", &data{
+				setup: setup{
+					oldMachineSetReplicas:     2,
+					newMachineSetReplicas:     3,
+					nodesWithUpdateSuccessful: 0,
+				},
+				expect: expect{
+					scaled: false,
+				},
+			}),
+			Entry("scale down newMachineSet as it has more replicas than deployment", &data{
+				setup: setup{
+					oldMachineSetReplicas:     2,
+					newMachineSetReplicas:     4,
+					nodesWithUpdateSuccessful: 0,
+				},
+				expect: expect{
+					scaled: true,
+				},
+			}),
+			Entry("scale up newMachineSet by transferring machines from oldMachineSet", &data{
+				setup: setup{
+					oldMachineSetReplicas:     2,
+					newMachineSetReplicas:     1,
+					nodesWithUpdateSuccessful: 1,
+				},
+				expect: expect{
+					scaled: true,
+				},
+			}),
+			Entry("scale up newMachineSet by scaling up newMachineSet if there are zero machines in oldMachineSet", &data{
+				setup: setup{
+					oldMachineSetReplicas:     0,
+					newMachineSetReplicas:     1,
+					nodesWithUpdateSuccessful: 0,
+				},
+				expect: expect{
+					scaled: true,
+				},
+			}),
+		)
+	})
+
+	Describe("reconcileOldMachineSetsInPlace", func() {
+		type setup struct {
+			oldMachineSetReplicas           int32
+			oldISAvailableMachines          int32
+			oldISCandidateForUpdateMachines int
+			oldISSelectedForUpdateMachines  int
+			newMachineSetReplicas           int32
+			newISAvailableMachines          int32
+		}
+		type expect struct {
+			scaled bool
+		}
+		type data struct {
+			setup  setup
+			expect expect
+		}
+
+		machineSets := newMachineSets(
+			2,
+			&machinev1.MachineTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "machineset-0",
+				},
+				Spec: machinev1.MachineSpec{
+					Class: machinev1.ClassSpec{
+						Kind: "MachineClass",
+						Name: "test-machine-class",
+					},
+				},
+			}, 1, 500, &machinev1.MachineSetStatus{AvailableReplicas: 1}, nil, nil, nil,
+		)
+
+		oldMachineSet := machineSets[0]
+		newMachineSet := machineSets[1]
+
+		deployment := &machinev1.MachineDeployment{
+			Spec: machinev1.MachineDeploymentSpec{
+				Replicas: int32(3),
+				Strategy: machinev1.MachineDeploymentStrategy{
+					Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+					InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+						UpdateConfiguration: machinev1.UpdateConfiguration{
+							MaxUnavailable: ptr.To(intstr.FromInt32(1)),
+							MaxSurge:       ptr.To(intstr.FromInt32(0)),
+						},
+					},
+				},
+			},
+		}
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				oldMachineSet.Spec.Replicas = data.setup.oldMachineSetReplicas
+				oldMachineSet.Status.AvailableReplicas = data.setup.oldISAvailableMachines
+				newMachineSet.Spec.Replicas = data.setup.newMachineSetReplicas
+				newMachineSet.Status.AvailableReplicas = data.setup.newISAvailableMachines
+
+				controlMachineObjects := []runtime.Object{}
+				controlMachineObjects = append(controlMachineObjects, oldMachineSet, newMachineSet)
+
+				machines := []*machinev1.Machine{}
+				machines = append(machines, newMachinesFromMachineSet(int(data.setup.oldMachineSetReplicas), oldMachineSet, &machinev1.MachineStatus{}, nil, nil)...)
+				machines = append(machines, newMachinesFromMachineSet(int(data.setup.newMachineSetReplicas), newMachineSet, &machinev1.MachineStatus{}, nil, nil)...)
+				for i, machine := range machines {
+					machine.Labels[machinev1.NodeLabelKey] = fmt.Sprintf("node-%d", i)
+				}
+
+				for _, o := range machines {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+
+				nodesCandidateForUpdate := 0
+				nodesSelectedForUpdate := 0
+				nodes := newNodes(len(machines), map[string]string{}, &corev1.NodeSpec{}, nil)
+				for i := range machines {
+					nodes[i].Labels = machines[i].Labels
+					if nodesCandidateForUpdate < data.setup.oldISCandidateForUpdateMachines {
+						nodes[i].Labels[machinev1.LabelKeyNodeCandidateForUpdate] = "true"
+						if nodesSelectedForUpdate < data.setup.oldISSelectedForUpdateMachines {
+							nodes[i].Labels[machinev1.LabelKeyNodeSelectedForUpdate] = "true"
+							nodesSelectedForUpdate++
+						}
+						nodesCandidateForUpdate++
+					}
+				}
+
+				targetCoreObjects := []runtime.Object{}
+				for _, o := range nodes {
+					targetCoreObjects = append(targetCoreObjects, o)
+				}
+
+				controller, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects)
+				defer trackers.Stop()
+				waitForCacheSync(stop, controller)
+
+				count, err := controller.reconcileOldMachineSetsInPlace(context.TODO(), []*machinev1.MachineSet{oldMachineSet, newMachineSet}, []*machinev1.MachineSet{oldMachineSet}, newMachineSet, deployment)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(count).To(Equal(data.expect.scaled))
+			},
+			Entry("no machines selected for update because there is no machines with candidate for update label", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 0,
+					oldISSelectedForUpdateMachines:  0,
+					newMachineSetReplicas:           1,
+					newISAvailableMachines:          1,
+				},
+				expect: expect{
+					scaled: false,
+				},
+			}),
+			Entry("no machines selected for update because there is not enough available replicas because some of new machines are unavailable", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 1,
+					oldISSelectedForUpdateMachines:  0,
+					newMachineSetReplicas:           1,
+					newISAvailableMachines:          0,
+				},
+				expect: expect{
+					scaled: false,
+				},
+			}),
+			Entry("no machines selected for update because there is still old replicas undergoing update respecting min available", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 1,
+					oldISSelectedForUpdateMachines:  1,
+					newMachineSetReplicas:           1,
+					newISAvailableMachines:          1,
+				},
+				expect: expect{
+					scaled: false,
+				},
+			}),
+			Entry("select one machine even though mutiple machines can be updated respecting min available", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 2,
+					oldISSelectedForUpdateMachines:  0,
+					newMachineSetReplicas:           1,
+					newISAvailableMachines:          1,
+				},
+				expect: expect{
+					scaled: true,
+				},
+			}),
+			Entry("scale down all old machine sets if new machine set already has replicas equal to deployment replicas", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 2,
+					oldISSelectedForUpdateMachines:  0,
+					newMachineSetReplicas:           3,
+					newISAvailableMachines:          3,
+				},
+				expect: expect{
+					scaled: true,
+				},
+			}),
+			Entry("no scale down because there is no old machine set with replicas > 0", &data{
+				setup: setup{
+					oldMachineSetReplicas:           0,
+					oldISAvailableMachines:          0,
+					oldISCandidateForUpdateMachines: 0,
+					oldISSelectedForUpdateMachines:  0,
+					newMachineSetReplicas:           3,
+					newISAvailableMachines:          3,
+				},
+				expect: expect{
+					scaled: false,
+				},
+			}),
+		)
+	})
+
+	Describe("selectNumOfMachineForUpdate", func() {
+		type setup struct {
+			oldMachineSetReplicas           int32
+			oldISAvailableMachines          int32
+			oldISCandidateForUpdateMachines int
+			newMachineSetReplicas           int32
+			newISAvailableMachines          int32
+		}
+		type expect struct {
+			count int32
+		}
+		type data struct {
+			setup  setup
+			action int32
+			expect expect
+		}
+
+		machineSets := newMachineSets(
+			2,
+			&machinev1.MachineTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "machineset-0",
+				},
+				Spec: machinev1.MachineSpec{
+					Class: machinev1.ClassSpec{
+						Kind: "MachineClass",
+						Name: "test-machine-class",
+					},
+				},
+			}, 1, 500, &machinev1.MachineSetStatus{AvailableReplicas: 1}, nil, nil, nil,
+		)
+
+		oldMachineSet := machineSets[0]
+		newMachineSet := machineSets[1]
+
+		deployment := &machinev1.MachineDeployment{
+			Spec: machinev1.MachineDeploymentSpec{
+				Replicas: int32(3),
+				Strategy: machinev1.MachineDeploymentStrategy{
+					Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+					InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+						UpdateConfiguration: machinev1.UpdateConfiguration{
+							MaxUnavailable: ptr.To(intstr.FromInt32(1)),
+							MaxSurge:       ptr.To(intstr.FromInt32(0)),
+						},
+					},
+				},
+			},
+		}
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				oldMachineSet.Spec.Replicas = data.setup.oldMachineSetReplicas
+				oldMachineSet.Status.AvailableReplicas = data.setup.oldISAvailableMachines
+				newMachineSet.Spec.Replicas = data.setup.newMachineSetReplicas
+				newMachineSet.Status.AvailableReplicas = data.setup.newISAvailableMachines
+
+				controlMachineObjects := []runtime.Object{}
+				controlMachineObjects = append(controlMachineObjects, oldMachineSet, newMachineSet)
+
+				machines := []*machinev1.Machine{}
+				machines = append(machines, newMachinesFromMachineSet(int(data.setup.oldMachineSetReplicas), oldMachineSet, &machinev1.MachineStatus{}, nil, nil)...)
+				for i := range data.setup.oldISCandidateForUpdateMachines {
+					machines[i].Labels[machinev1.LabelKeyNodeCandidateForUpdate] = "true"
+				}
+
+				machines = append(machines, newMachinesFromMachineSet(int(data.setup.newMachineSetReplicas), newMachineSet, &machinev1.MachineStatus{}, nil, nil)...)
+				for i, machine := range machines {
+					machine.Labels[machinev1.NodeLabelKey] = fmt.Sprintf("node-%d", i)
+				}
+
+				for _, o := range machines {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+
+				nodes := newNodes(len(machines), map[string]string{}, &corev1.NodeSpec{}, nil)
+				for i := range machines {
+					nodes[i].Labels = machines[i].Labels
+				}
+
+				targetCoreObjects := []runtime.Object{}
+				for _, o := range nodes {
+					targetCoreObjects = append(targetCoreObjects, o)
+				}
+
+				controller, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects)
+				defer trackers.Stop()
+				waitForCacheSync(stop, controller)
+
+				count, err := controller.selectNumOfMachineForUpdate(context.TODO(), []*machinev1.MachineSet{oldMachineSet, newMachineSet}, []*machinev1.MachineSet{oldMachineSet}, newMachineSet, deployment, data.action)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(count).To(Equal(data.expect.count))
+			},
+			Entry("no machines selected for update because there is no machines with candidate for update label", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 0,
+					newMachineSetReplicas:           1,
+					newISAvailableMachines:          1,
+				},
+				action: 0,
+				expect: expect{
+					count: 0,
+				},
+			}),
+			Entry("no machines selected for update because there is not enough available replicas because some of new machines are unavailable", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 1,
+					newMachineSetReplicas:           1,
+					newISAvailableMachines:          0,
+				},
+				action: 0,
+				expect: expect{
+					count: 0,
+				},
+			}),
+			Entry("no machines selected for update because there is still old replicas undergoing update respecting min avaialble", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 1,
+					newMachineSetReplicas:           1,
+					newISAvailableMachines:          1,
+				},
+				action: 1,
+				expect: expect{
+					count: 0,
+				},
+			}),
+			Entry("select one machine even though multiple machines can be updated respecting min available", &data{
+				setup: setup{
+					oldMachineSetReplicas:           2,
+					oldISAvailableMachines:          2,
+					oldISCandidateForUpdateMachines: 2,
+					newMachineSetReplicas:           1,
+					newISAvailableMachines:          1,
+				},
+				action: 0,
+				expect: expect{
+					count: 1,
+				},
+			}),
+		)
+	})
+
+	Describe("labelNodesBackingMachineSets", func() {
+		type setup struct {
+			nodes       []*corev1.Node
+			machineSets []*machinev1.MachineSet
+			machines    []*machinev1.Machine
+		}
+		type expect struct {
+			machines []*machinev1.Machine
+			nodes    []*corev1.Node
+			err      bool
+		}
+		type data struct {
+			setup  setup
+			action []*machinev1.MachineSet
+			expect expect
+		}
+		objMeta := &metav1.ObjectMeta{
+			Namespace: testNamespace,
+		}
+		machineSets := newMachineSets(
+			1,
+			&machinev1.MachineTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "machineset-0",
+					Labels: map[string]string{
+						"key": "value",
+					},
+				},
+				Spec: machinev1.MachineSpec{
+					Class: machinev1.ClassSpec{
+						Kind: "MachineClass",
+						Name: "test-machine-class",
+					},
+				},
+			}, 3, 500, nil, nil, nil, nil,
+		)
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				controlMachineObjects := []runtime.Object{}
+				for _, o := range data.setup.machineSets {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+				for _, o := range data.setup.machines {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+
+				targetCoreObjects := []runtime.Object{}
+				for _, o := range data.setup.nodes {
+					targetCoreObjects = append(targetCoreObjects, o)
+				}
+
+				controller, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects)
+				defer trackers.Stop()
+				waitForCacheSync(stop, controller)
+
+				err := controller.labelNodesBackingMachineSets(context.TODO(), data.action, "key", "value")
+				if !data.expect.err {
+					Expect(err).To(BeNil())
+				} else {
+					Expect(err).To(HaveOccurred())
+				}
+
+				for _, expectedMachine := range data.expect.machines {
+					actualMachine, err := controller.controlMachineClient.Machines(testNamespace).Get(context.TODO(), expectedMachine.Name, metav1.GetOptions{})
+					Expect(err).ToNot(HaveOccurred())
+					Expect(actualMachine.Labels).Should(Equal(expectedMachine.Labels))
+				}
+
+				for _, expectedNode := range data.expect.nodes {
+					actualNode, err := controller.targetCoreClient.CoreV1().Nodes().Get(context.TODO(), expectedNode.Name, metav1.GetOptions{})
+					Expect(err).ToNot(HaveOccurred())
+					Expect(actualNode.Labels).Should(Equal(expectedNode.Labels))
+				}
+			},
+
+			Entry("labels on nodes backing machineSet", &data{
+				setup: setup{
+					machines: newMachinesFromMachineSet(1, machineSets[0], &machinev1.MachineStatus{}, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}),
+					nodes:    newNodes(1, nil, &corev1.NodeSpec{}, nil),
+				},
+				action: newMachineSets(
+					1,
+					&machinev1.MachineTemplateSpec{
+						ObjectMeta: *newObjectMeta(objMeta, 0),
+						Spec: machinev1.MachineSpec{
+							Class: machinev1.ClassSpec{
+								Kind: "MachineClass",
+								Name: "test-machine-class",
+							},
+						},
+					}, 3, 500, nil, nil, nil, nil,
+				),
+				expect: expect{
+					machines: newMachinesFromMachineSet(1, machineSets[0], &machinev1.MachineStatus{}, nil, map[string]string{machinev1.NodeLabelKey: "node-0", "key": "value"}),
+					nodes:    newNodes(1, map[string]string{"key": "value"}, &corev1.NodeSpec{}, nil),
+					err:      false,
+				},
+			}),
+		)
+	})
+
+	Describe("getMachinesUndergoingUpdate", func() {
+		type setup struct {
+			machineSets []*machinev1.MachineSet
+			machines    []*machinev1.Machine
+		}
+		type expect struct {
+			count int32
+			err   bool
+		}
+		type data struct {
+			setup  setup
+			action []*machinev1.MachineSet
+			expect expect
+		}
+
+		objMeta := &metav1.ObjectMeta{
+			Namespace: testNamespace,
+		}
+
+		machineSets := newMachineSets(
+			1,
+			&machinev1.MachineTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "machineset-0",
+				},
+				Spec: machinev1.MachineSpec{
+					Class: machinev1.ClassSpec{
+						Kind: "MachineClass",
+						Name: "test-machine-class",
+					},
+				},
+			}, 3, 500, nil, nil, nil, nil,
+		)
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				controlMachineObjects := []runtime.Object{}
+				for _, o := range data.setup.machineSets {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+
+				for i, machine := range data.setup.machines {
+					if machine.Labels == nil {
+						machine.Labels = map[string]string{}
+					}
+					machine.Labels[machinev1.NodeLabelKey] = fmt.Sprintf("node-%d", i)
+				}
+
+				for _, o := range data.setup.machines {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+
+				nodes := newNodes(len(data.setup.machines), map[string]string{}, &corev1.NodeSpec{}, nil)
+				for i := range data.setup.machines {
+					nodes[i].Labels = data.setup.machines[i].Labels
+				}
+
+				targetCoreObjects := []runtime.Object{}
+				for _, o := range nodes {
+					targetCoreObjects = append(targetCoreObjects, o)
+				}
+
+				controller, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects)
+				defer trackers.Stop()
+				waitForCacheSync(stop, controller)
+
+				count, err := controller.getMachinesUndergoingUpdate(data.action)
+				if !data.expect.err {
+					Expect(err).To(BeNil())
+				} else {
+					Expect(err).To(HaveOccurred())
+				}
+
+				Expect(count).To(Equal(data.expect.count))
+			},
+			Entry("no machines undergoing update", &data{
+				setup: setup{
+					machineSets: machineSets,
+					machines:    newMachinesFromMachineSet(1, machineSets[0], &machinev1.MachineStatus{}, nil, nil),
+				},
+				action: newMachineSets(
+					1,
+					&machinev1.MachineTemplateSpec{
+						ObjectMeta: *newObjectMeta(objMeta, 0),
+						Spec: machinev1.MachineSpec{
+							Class: machinev1.ClassSpec{
+								Kind: "MachineClass",
+								Name: "test-machine-class",
+							},
+						},
+					}, 3, 500, nil, nil, nil, nil,
+				),
+				expect: expect{
+					count: 0,
+					err:   false,
+				},
+			}),
+			Entry("one machine undergoing update", &data{
+				setup: setup{
+					machineSets: machineSets,
+					machines:    newMachinesFromMachineSet(1, machineSets[0], &machinev1.MachineStatus{}, nil, map[string]string{machinev1.LabelKeyNodeSelectedForUpdate: "true"}),
+				},
+				action: newMachineSets(
+					1,
+					&machinev1.MachineTemplateSpec{
+						ObjectMeta: *newObjectMeta(objMeta, 0),
+						Spec: machinev1.MachineSpec{
+							Class: machinev1.ClassSpec{
+								Kind: "MachineClass",
+								Name: "test-machine-class",
+							},
+						},
+					}, 3, 500, nil, nil, nil, nil,
+				),
+				expect: expect{
+					count: 1,
+					err:   false,
+				},
+			}),
+			Entry("multiple machines undergoing update", &data{
+				setup: setup{
+					machineSets: machineSets,
+					machines:    newMachinesFromMachineSet(2, machineSets[0], &machinev1.MachineStatus{}, nil, map[string]string{machinev1.LabelKeyNodeSelectedForUpdate: "true"}),
+				},
+				action: newMachineSets(
+					1,
+					&machinev1.MachineTemplateSpec{
+						ObjectMeta: *newObjectMeta(objMeta, 0),
+						Spec: machinev1.MachineSpec{
+							Class: machinev1.ClassSpec{
+								Kind: "MachineClass",
+								Name: "test-machine-class",
+							},
+						},
+					}, 3, 500, nil, nil, nil, nil,
+				),
+				expect: expect{
+					count: 2,
+					err:   false,
+				},
+			}),
+			Entry("only one machines is undergoing update", &data{
+				setup: setup{
+					machineSets: machineSets,
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.LabelKeyNodeSelectedForUpdate: "true",
+								},
+							},
+						},
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-1",
+								Namespace: testNamespace,
+							},
+						},
+					},
+				},
+				action: newMachineSets(
+					1,
+					&machinev1.MachineTemplateSpec{
+						ObjectMeta: *newObjectMeta(objMeta, 0),
+						Spec: machinev1.MachineSpec{
+							Class: machinev1.ClassSpec{
+								Kind: "MachineClass",
+								Name: "test-machine-class",
+							},
+						},
+					}, 3, 500, nil, nil, nil, nil,
+				),
+				expect: expect{
+					count: 1,
+					err:   false,
+				},
+			}),
+			Entry("there are no machines", &data{
+				setup: setup{
+					machines:    []*machinev1.Machine{},
+					machineSets: []*machinev1.MachineSet{},
+				},
+				action: []*machinev1.MachineSet{},
+				expect: expect{
+					count: 0,
+					err:   false,
+				},
+			}),
+		)
+	})
+
+	Describe("getMachinesForDrain", func() {
+		type setup struct {
+			machineSet *machinev1.MachineSet
+			machines   []*machinev1.Machine
+		}
+		type expect struct {
+			machines []*machinev1.Machine
+			err      bool
+		}
+		type data struct {
+			setup  setup
+			action int32
+			expect expect
+		}
+
+		machineSet := newMachineSets(
+			1,
+			&machinev1.MachineTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "machineset-0",
+				},
+				Spec: machinev1.MachineSpec{
+					Class: machinev1.ClassSpec{
+						Kind: "MachineClass",
+						Name: "test-machine-class",
+					},
+				},
+			}, 3, 500, nil, nil, nil, nil,
+		)[0]
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				controlMachineObjects := []runtime.Object{data.setup.machineSet}
+				for _, o := range data.setup.machines {
+					controlMachineObjects = append(controlMachineObjects, o)
+				}
+
+				nodes := newNodes(len(data.setup.machines), map[string]string{}, &corev1.NodeSpec{}, nil)
+				for i := range data.setup.machines {
+					nodes[i].Labels = data.setup.machines[i].Labels
+				}
+
+				targetCoreObjects := []runtime.Object{}
+				for _, o := range nodes {
+					targetCoreObjects = append(targetCoreObjects, o)
+				}
+
+				controller, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects)
+				defer trackers.Stop()
+				waitForCacheSync(stop, controller)
+
+				machines, err := controller.getMachinesForDrain(data.setup.machineSet, data.action)
+				if !data.expect.err {
+					Expect(err).To(BeNil())
+				} else {
+					Expect(err).To(HaveOccurred())
+				}
+
+				Expect(len(machines)).To(Equal(len(data.expect.machines)))
+			},
+			Entry("select machines for drain", &data{
+				setup: setup{
+					machineSet: machineSet,
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-1",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 1),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+									machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+								},
+							},
+						},
+					},
+				},
+				action: 1,
+				expect: expect{
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+					},
+					err: false,
+				},
+			}),
+			Entry("select all machines for drain", &data{
+				setup: setup{
+					machineSet: machineSet,
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-1",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 1),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+					},
+				},
+				action: 2,
+				expect: expect{
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-1",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 1),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+					},
+					err: false,
+				},
+			}),
+			Entry("select only required count of machines even though more machines can be selected", &data{
+				setup: setup{
+					machineSet: machineSet,
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-1",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 1),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+					},
+				},
+				action: 1,
+				expect: expect{
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+					},
+					err: false,
+				},
+			}),
+			Entry("select only machines which has label candidate for update", &data{
+				setup: setup{
+					machineSet: machineSet,
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-1",
+								Namespace: testNamespace,
+							},
+						},
+					},
+				},
+				action: 2,
+				expect: expect{
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								},
+							},
+						},
+					},
+					err: false,
+				},
+			}),
+			Entry("no machines available for drain all are already selected", &data{
+				setup: setup{
+					machineSet: machineSet,
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 0),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+									machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+								},
+							},
+						},
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-1",
+								Namespace: testNamespace,
+								Labels: map[string]string{
+									machinev1.NodeLabelKey:                   fmt.Sprintf("node-%d", 1),
+									machinev1.LabelKeyNodeCandidateForUpdate: "true",
+									machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+								},
+							},
+						},
+					},
+				},
+				action: 1,
+				expect: expect{
+					machines: []*machinev1.Machine{},
+					err:      false,
+				},
+			}),
+			Entry("no machines available for drain", &data{
+				setup: setup{
+					machineSet: machineSet,
+					machines: []*machinev1.Machine{
+						{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "machine-0",
+								Namespace: testNamespace,
+							},
+						},
+					},
+				},
+				action: 1,
+				expect: expect{
+					machines: []*machinev1.Machine{},
+					err:      false,
+				},
+			}),
+		)
+	})
+})
diff --git a/pkg/controller/deployment_rollback_test.go b/pkg/controller/deployment_rollback_test.go
index 13b684fd4..7a63caa7c 100644
--- a/pkg/controller/deployment_rollback_test.go
+++ b/pkg/controller/deployment_rollback_test.go
@@ -125,6 +125,7 @@ var _ = Describe("deployment_rollback", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{
 								{
@@ -178,6 +179,7 @@ var _ = Describe("deployment_rollback", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
diff --git a/pkg/controller/deployment_rolling_test.go b/pkg/controller/deployment_rolling_test.go
index ef0a3b45b..ff468769a 100644
--- a/pkg/controller/deployment_rolling_test.go
+++ b/pkg/controller/deployment_rolling_test.go
@@ -6,6 +6,7 @@ package controller
 
 import (
 	"context"
+
 	machinev1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
 	"github.com/gardener/machine-controller-manager/pkg/controller/autoscaler"
 	. "github.com/onsi/ginkgo/v2"
@@ -120,6 +121,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
@@ -167,6 +169,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{
 								{
@@ -283,6 +286,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
@@ -335,6 +339,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
@@ -386,6 +391,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
@@ -437,6 +443,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						0,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
@@ -579,6 +586,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
@@ -633,6 +641,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
@@ -687,6 +696,7 @@ var _ = Describe("deployment_rolling", func() {
 					),
 					nodes: newNodes(
 						1,
+						nil,
 						&corev1.NodeSpec{
 							Taints: []corev1.Taint{},
 						},
diff --git a/pkg/controller/deployment_test.go b/pkg/controller/deployment_test.go
index d378c8fdc..e1d6565f9 100644
--- a/pkg/controller/deployment_test.go
+++ b/pkg/controller/deployment_test.go
@@ -8,9 +8,11 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"time"
+
 	"github.com/gardener/machine-controller-manager/pkg/util/annotations"
 	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
-	"time"
+	"k8s.io/utils/ptr"
 
 	machinev1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
 	. "github.com/onsi/ginkgo/v2"
@@ -604,6 +606,177 @@ var _ = Describe("machineDeployment", func() {
 		)
 	})
 
+	Describe("#updateMachineToMachineDeployment", func() {
+		var (
+			testMachineDeployment *machinev1.MachineDeployment
+			testMachineSet        *machinev1.MachineSet
+			testMachine           *machinev1.Machine
+			updatedMachine        *machinev1.Machine
+		)
+
+		BeforeEach(func() {
+			testMachineSet = &machinev1.MachineSet{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "MachineSet-test",
+					Namespace: testNamespace,
+					Labels: map[string]string{
+						"test-label": "test-label",
+					},
+					UID: "1234567",
+					OwnerReferences: []metav1.OwnerReference{
+						{
+							Kind:       "MachineDeployment",
+							Name:       "MachineDeployment-test",
+							UID:        "1234567",
+							Controller: ptr.To(true),
+						},
+					},
+				},
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "MachineSet",
+					APIVersion: "machine.sapcloud.io/v1alpha1",
+				},
+				Spec: machinev1.MachineSetSpec{
+					Replicas: 3,
+					Template: machinev1.MachineTemplateSpec{
+						ObjectMeta: metav1.ObjectMeta{
+							Labels: map[string]string{
+								"test-label": "test-label",
+							},
+						},
+						Spec: machinev1.MachineSpec{
+							Class: machinev1.ClassSpec{
+								Name: "MachineClass-test",
+								Kind: "MachineClass",
+							},
+						},
+					},
+					Selector: &metav1.LabelSelector{
+						MatchLabels: map[string]string{
+							"test-label": "test-label",
+						},
+					},
+				},
+			}
+
+			testMachineDeployment = &machinev1.MachineDeployment{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "MachineDeployment-test",
+					Namespace: testNamespace,
+					Labels: map[string]string{
+						"test-label": "test-label",
+					},
+					UID: "1234567",
+				},
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "MachineDeployment",
+					APIVersion: "machine.sapcloud.io/v1alpha1",
+				},
+				Spec: machinev1.MachineDeploymentSpec{
+					Replicas: testMachineSet.Spec.Replicas,
+					Template: testMachineSet.Spec.Template,
+					Selector: testMachineSet.Spec.Selector.DeepCopy(),
+				},
+			}
+
+			testMachine = &machinev1.Machine{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "Machine-test",
+					Namespace: testNamespace,
+					Labels: map[string]string{
+						"test-label": "test-label",
+					},
+					OwnerReferences: []metav1.OwnerReference{
+						{
+							Kind:       "MachineSet",
+							Name:       "MachineSet-test",
+							UID:        "1234567",
+							Controller: ptr.To(true),
+						},
+					},
+				},
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "Machine",
+					APIVersion: "machine.sapcloud.io/v1alpha1",
+				},
+			}
+
+			updatedMachine = testMachine.DeepCopy()
+			updatedMachine.Status.Conditions = []corev1.NodeCondition{
+				{
+					Type:   machinev1.NodeInPlaceUpdate,
+					Status: corev1.ConditionTrue,
+					Reason: machinev1.UpdateSuccessful,
+				},
+			}
+		})
+
+		DescribeTable("Should enqueue the machineDeployment",
+			func(preset func(), queueLength int) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				preset()
+
+				objects := []runtime.Object{}
+				objects = append(objects, testMachineDeployment, testMachineSet)
+				c, trackers := createController(stop, testNamespace, objects, nil, nil)
+
+				defer trackers.Stop()
+				waitForCacheSync(stop, c)
+				c.updateMachineToMachineDeployment(testMachine, updatedMachine)
+
+				waitForCacheSync(stop, c)
+				Expect(c.machineDeploymentQueue.Len()).To(Equal(queueLength))
+			},
+			Entry("not enqueue when no conditions are set",
+				func() {
+					updatedMachine.Status.Conditions = nil
+				}, 0,
+			),
+			Entry("not enqueue when condition reason is not UpdateSuccessful",
+				func() {
+					updatedMachine.Status.Conditions = []corev1.NodeCondition{
+						{
+							Type:   machinev1.NodeInPlaceUpdate,
+							Status: corev1.ConditionTrue,
+							Reason: machinev1.UpdateFailed,
+						},
+					}
+				}, 0,
+			),
+			Entry("enqueue when condition reason is UpdateSuccessful",
+				func() {
+					updatedMachine.Status.Conditions = []corev1.NodeCondition{
+						{
+							Type:   machinev1.NodeInPlaceUpdate,
+							Status: corev1.ConditionTrue,
+							Reason: machinev1.UpdateSuccessful,
+						},
+					}
+				}, 1,
+			),
+			Entry("should enqueue when condition reason is changed to UpdateSuccessful",
+				func() {
+					testMachine.Status.Conditions = []corev1.NodeCondition{
+						{
+							Type:   machinev1.NodeInPlaceUpdate,
+							Status: corev1.ConditionTrue,
+							Reason: machinev1.ReadyForUpdate,
+						},
+					}
+					updatedMachine.Status.Conditions = []corev1.NodeCondition{
+						{
+							Type:   machinev1.NodeInPlaceUpdate,
+							Status: corev1.ConditionTrue,
+							Reason: machinev1.UpdateSuccessful,
+						},
+					}
+				}, 1,
+			),
+		)
+	})
+
 	Describe("#deleteMachineToMachineDeployment", func() {
 		var (
 			testMachineDeployment *machinev1.MachineDeployment
@@ -1039,7 +1212,7 @@ var _ = Describe("machineDeployment", func() {
 
 				defer trackers.Stop()
 				waitForCacheSync(stop, c)
-				actualMachineDeployment := c.getMachineDeploymentForMachine(context.Background(), testMachine)
+				actualMachineDeployment := c.getMachineDeploymentForMachine(testMachine)
 
 				waitForCacheSync(stop, c)
 				if expectedMachineDeploymentName != "" {
diff --git a/pkg/controller/deployment_util_test.go b/pkg/controller/deployment_util_test.go
index 187c20e4f..df6ec988d 100644
--- a/pkg/controller/deployment_util_test.go
+++ b/pkg/controller/deployment_util_test.go
@@ -12,6 +12,7 @@ import (
 	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
+	"k8s.io/utils/ptr"
 )
 
 var _ = Describe("deployment_util", func() {
@@ -80,6 +81,184 @@ var _ = Describe("deployment_util", func() {
 		}
 	})
 
+	DescribeTable("#MaxUnavailable",
+		func(strategy machinev1.MachineDeploymentStrategy, replicas int, expected int) {
+			machineDeployment.Spec.Strategy = strategy
+			machineDeployment.Spec.Replicas = int32(replicas)
+			Expect(MaxUnavailable(*machineDeployment)).To(Equal(int32(expected)))
+		},
+
+		Entry("strategy is neither RollingUpdate nor InPlaceUpdate", machinev1.MachineDeploymentStrategy{Type: ""}, 2, 0),
+		Entry("strategy is RollingUpdate and replicas is zeros", machinev1.MachineDeploymentStrategy{Type: machinev1.RollingUpdateMachineDeploymentStrategyType}, 0, 0),
+
+		// Case: RollingUpdate strategy with valid maxUnavailable and replicas
+		Entry("strategy is RollingUpdate with maxUnavailable=1, replicas=5",
+			machinev1.MachineDeploymentStrategy{
+				Type: machinev1.RollingUpdateMachineDeploymentStrategyType,
+				RollingUpdate: &machinev1.RollingUpdateMachineDeployment{
+					UpdateConfiguration: machinev1.UpdateConfiguration{
+						MaxSurge:       ptr.To(intstr.FromInt(1)),
+						MaxUnavailable: ptr.To(intstr.FromInt(1)),
+					},
+				},
+			}, 5, 1),
+
+		// Case: RollingUpdate strategy with maxUnavailable greater than replicas
+		Entry("strategy is RollingUpdate with maxUnavailable=10, replicas=5",
+			machinev1.MachineDeploymentStrategy{
+				Type: machinev1.RollingUpdateMachineDeploymentStrategyType,
+				RollingUpdate: &machinev1.RollingUpdateMachineDeployment{
+					UpdateConfiguration: machinev1.UpdateConfiguration{
+						MaxSurge:       ptr.To(intstr.FromInt(2)),
+						MaxUnavailable: ptr.To(intstr.FromInt(10)),
+					},
+				},
+			}, 5, 5),
+
+		// Case: InPlaceUpdate strategy with ManualUpdate=true
+		Entry("strategy is InPlaceUpdate with ManualUpdate=true, maxUnavailable=2, replicas=5",
+			machinev1.MachineDeploymentStrategy{
+				Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+				InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+					UpdateConfiguration: machinev1.UpdateConfiguration{
+						MaxSurge:       ptr.To(intstr.FromInt(1)),
+						MaxUnavailable: ptr.To(intstr.FromInt(2)),
+					},
+					OrchestrationType: machinev1.OrchestrationTypeManual,
+				},
+			}, 5, 2),
+
+		// Case: InPlaceUpdate strategy with ManualUpdate=false and maxUnavailable > replicas
+		Entry("strategy is InPlaceUpdate with ManualUpdate=false, maxUnavailable=10, replicas=3",
+			machinev1.MachineDeploymentStrategy{
+				Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+				InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+					UpdateConfiguration: machinev1.UpdateConfiguration{
+						MaxSurge:       ptr.To(intstr.FromInt(1)),
+						MaxUnavailable: ptr.To(intstr.FromInt(10)),
+					},
+					OrchestrationType: machinev1.OrchestrationTypeAuto,
+				},
+			}, 3, 3),
+
+		// Case: InPlaceUpdate strategy with ManualUpdate=true and replicas=0
+		Entry("strategy is InPlaceUpdate with ManualUpdate=true, replicas=0",
+			machinev1.MachineDeploymentStrategy{
+				Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+				InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+					OrchestrationType: machinev1.OrchestrationTypeManual,
+				},
+			}, 0, 0),
+	)
+
+	Describe("#NewISNewReplicas", func() {
+		type data struct {
+			replicas      int
+			oldISReplicas int
+			newISReplicas int
+			expected      int
+			shouldError   bool
+		}
+
+		DescribeTable("#table",
+			func(strategy machinev1.MachineDeploymentStrategy, testData data) {
+				// Setup MachineDeployment and MachineSets
+				deployment := &machinev1.MachineDeployment{
+					Spec: machinev1.MachineDeploymentSpec{
+						Replicas: int32(testData.replicas),
+						Strategy: strategy,
+					},
+				}
+
+				newIS := &machinev1.MachineSet{
+					Spec:   machinev1.MachineSetSpec{Replicas: int32(testData.newISReplicas)},
+					Status: machinev1.MachineSetStatus{Replicas: int32(testData.newISReplicas)},
+				}
+
+				allISs := []*machinev1.MachineSet{
+					newIS,
+					{Spec: machinev1.MachineSetSpec{Replicas: int32(testData.oldISReplicas)}, Status: machinev1.MachineSetStatus{Replicas: int32(testData.oldISReplicas)}},
+				}
+
+				result, err := NewISNewReplicas(deployment, allISs, newIS)
+
+				if testData.shouldError {
+					Expect(err).To(HaveOccurred())
+				} else {
+					Expect(err).NotTo(HaveOccurred())
+					Expect(result).To(Equal(int32(testData.expected)))
+				}
+			},
+
+			Entry("RollingUpdate, scale up within max surge",
+				machinev1.MachineDeploymentStrategy{
+					Type: machinev1.RollingUpdateMachineDeploymentStrategyType,
+					RollingUpdate: &machinev1.RollingUpdateMachineDeployment{
+						UpdateConfiguration: machinev1.UpdateConfiguration{
+							MaxSurge: ptr.To(intstr.FromInt(2)),
+						},
+					},
+				}, data{replicas: 5, oldISReplicas: 4, newISReplicas: 2, expected: 3, shouldError: false},
+			),
+
+			Entry("RollingUpdate, max surge reached",
+				machinev1.MachineDeploymentStrategy{
+					Type: machinev1.RollingUpdateMachineDeploymentStrategyType,
+					RollingUpdate: &machinev1.RollingUpdateMachineDeployment{
+						UpdateConfiguration: machinev1.UpdateConfiguration{
+							MaxSurge: ptr.To(intstr.FromInt(2)),
+						},
+					},
+				}, data{replicas: 5, oldISReplicas: 4, newISReplicas: 3, expected: 3, shouldError: false},
+			),
+
+			Entry("Recreate strategy",
+				machinev1.MachineDeploymentStrategy{
+					Type: machinev1.RecreateMachineDeploymentStrategyType,
+				}, data{replicas: 5, oldISReplicas: 4, newISReplicas: 2, expected: 5, shouldError: false},
+			),
+
+			Entry("InPlaceUpdate with ManualUpdate=true",
+				machinev1.MachineDeploymentStrategy{
+					Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+					InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+						OrchestrationType: machinev1.OrchestrationTypeManual,
+					},
+				}, data{replicas: 5, oldISReplicas: 4, newISReplicas: 2, expected: 0, shouldError: false},
+			),
+
+			Entry("InPlaceUpdate, scale up within max surge",
+				machinev1.MachineDeploymentStrategy{
+					Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+					InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+						UpdateConfiguration: machinev1.UpdateConfiguration{
+							MaxSurge: ptr.To(intstr.FromInt(2)),
+						},
+						OrchestrationType: machinev1.OrchestrationTypeAuto,
+					},
+				}, data{replicas: 5, oldISReplicas: 4, newISReplicas: 2, expected: 3, shouldError: false},
+			),
+
+			Entry("InPlaceUpdate with ManualUpdate=false, max surge already reached",
+				machinev1.MachineDeploymentStrategy{
+					Type: machinev1.InPlaceUpdateMachineDeploymentStrategyType,
+					InPlaceUpdate: &machinev1.InPlaceUpdateMachineDeployment{
+						UpdateConfiguration: machinev1.UpdateConfiguration{
+							MaxSurge: ptr.To(intstr.FromInt(1)),
+						},
+						OrchestrationType: machinev1.OrchestrationTypeAuto,
+					},
+				}, data{replicas: 5, oldISReplicas: 4, newISReplicas: 2, expected: 2, shouldError: false},
+			),
+
+			Entry("Unsupported strategy type",
+				machinev1.MachineDeploymentStrategy{
+					Type: "UnsupportedType",
+				}, data{replicas: 5, oldISReplicas: 4, newISReplicas: 2, expected: 0, shouldError: true},
+			),
+		)
+	})
+
 	Describe("#SetNewMachineSetNodeTemplate", func() {
 		It("when nodeTemplate is updated in MachineSet", func() {
 			stop := make(chan struct{})
@@ -324,4 +503,96 @@ var _ = Describe("deployment_util", func() {
 		})
 
 	})
+
+	Describe("#MergeStringMaps", func() {
+		It("should merge maps correctly with no conflicts", func() {
+			oldMap := map[string]string{
+				"key1": "value1",
+				"key2": "value2",
+			}
+			newMap1 := map[string]string{
+				"key3": "value3",
+			}
+			newMap2 := map[string]string{
+				"key4": "value4",
+			}
+
+			expected := map[string]string{
+				"key1": "value1",
+				"key2": "value2",
+				"key3": "value3",
+				"key4": "value4",
+			}
+
+			result := MergeStringMaps(oldMap, newMap1, newMap2)
+			Expect(result).To(Equal(expected))
+		})
+
+		It("should overwrite values from oldMap with values from newMaps", func() {
+			oldMap := map[string]string{
+				"key1": "value1",
+				"key2": "value2",
+			}
+			newMap1 := map[string]string{
+				"key2": "newValue2",
+			}
+			newMap2 := map[string]string{
+				"key1": "newValue1",
+			}
+
+			expected := map[string]string{
+				"key1": "newValue1",
+				"key2": "newValue2",
+			}
+
+			result := MergeStringMaps(oldMap, newMap1, newMap2)
+			Expect(result).To(Equal(expected))
+		})
+
+		It("should handle nil oldMap correctly", func() {
+			var oldMap map[string]string
+			newMap1 := map[string]string{
+				"key1": "value1",
+			}
+			newMap2 := map[string]string{
+				"key2": "value2",
+			}
+
+			expected := map[string]string{
+				"key1": "value1",
+				"key2": "value2",
+			}
+
+			result := MergeStringMaps(oldMap, newMap1, newMap2)
+			Expect(result).To(Equal(expected))
+		})
+
+		It("should handle nil newMaps correctly", func() {
+			oldMap := map[string]string{
+				"key1": "value1",
+			}
+			var newMap1 map[string]string
+			newMap2 := map[string]string{
+				"key2": "value2",
+			}
+
+			expected := map[string]string{
+				"key1": "value1",
+				"key2": "value2",
+			}
+
+			result := MergeStringMaps(oldMap, newMap1, newMap2)
+			Expect(result).To(Equal(expected))
+		})
+
+		It("should return an empty map if all inputs are nil", func() {
+			var oldMap map[string]string
+			var newMap1 map[string]string
+			var newMap2 map[string]string
+			var expected map[string]string
+
+			result := MergeStringMaps(oldMap, newMap1, newMap2)
+			Expect(result).To(Equal(expected))
+		})
+	})
 })
diff --git a/pkg/controller/machineset_test.go b/pkg/controller/machineset_test.go
index a8d9a2ff9..b58c9f47a 100644
--- a/pkg/controller/machineset_test.go
+++ b/pkg/controller/machineset_test.go
@@ -7,19 +7,18 @@ package controller
 import (
 	"context"
 	"errors"
-	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
 	"sync"
 	"time"
 
-	"k8s.io/utils/pointer"
-
-	machinev1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
-
 	k8sError "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/utils/pointer"
+
+	machinev1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
+	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
 )
 
 const (
@@ -568,6 +567,7 @@ var _ = Describe("machineset", func() {
 			testActiveMachine2 *machinev1.Machine
 			testActiveMachine3 *machinev1.Machine
 			testActiveMachine4 *machinev1.Machine
+			testActiveMachine5 *machinev1.Machine
 		)
 
 		BeforeEach(func() {
@@ -871,7 +871,177 @@ var _ = Describe("machineset", func() {
 			// replica count is still maintained.
 			Expect(len(afterMachines.Items)).To(Equal(int(testMachineSet.Spec.Replicas)))
 			Expect(err).Should(BeNil())
+		})
+
+		Describe("machine with update-result label", func() {
+			// Testcase: ActiveMachines + MachinesWithUpdateSuccessfulLabel < DesiredMachines
+			It("should create new machines and should not return errors.", func() {
+				stop := make(chan struct{})
+				defer close(stop)
 
+				testActiveMachine3 = &machinev1.Machine{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "machine-3",
+						Namespace: testNamespace,
+						UID:       "12345610",
+						Labels: map[string]string{
+							"test-label":                             "test-label",
+							"node.machine.sapcloud.io/update-result": "successful",
+						},
+					},
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Machine",
+						APIVersion: "machine.sapcloud.io/v1alpha1",
+					},
+					Status: machinev1.MachineStatus{
+						CurrentStatus: machinev1.CurrentStatus{
+							Phase: MachineRunning,
+						},
+					},
+				}
+
+				objects := []runtime.Object{}
+				objects = append(objects, testMachineSet, testActiveMachine1, testActiveMachine3)
+				c, trackers := createController(stop, testNamespace, objects, nil, nil)
+				defer trackers.Stop()
+				waitForCacheSync(stop, c)
+
+				machines, _ := c.controlMachineClient.Machines(testNamespace).List(context.TODO(), metav1.ListOptions{})
+				Expect(len(machines.Items)).To(Equal(int(testMachineSet.Spec.Replicas) - 1))
+
+				activeMachines := []*machinev1.Machine{testActiveMachine1, testActiveMachine3}
+				Err := c.manageReplicas(context.TODO(), activeMachines, testMachineSet)
+				waitForCacheSync(stop, c)
+				machines, _ = c.controlMachineClient.Machines(testNamespace).List(context.TODO(), metav1.ListOptions{})
+				Expect(len(machines.Items)).To(Equal(int(testMachineSet.Spec.Replicas)))
+				Expect(Err).Should(BeNil())
+			})
+
+			// Testcase: ActiveMachines + MachinesWithUpdateSuccessfulLabel = DesiredMachines
+			It("should not create or delete machines and should not return error", func() {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				testActiveMachine3 = &machinev1.Machine{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "machine-3",
+						Namespace: testNamespace,
+						UID:       "12345610",
+						Labels: map[string]string{
+							"test-label":                             "test-label",
+							"node.machine.sapcloud.io/update-result": "successful",
+						},
+					},
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Machine",
+						APIVersion: "machine.sapcloud.io/v1alpha1",
+					},
+					Status: machinev1.MachineStatus{
+						CurrentStatus: machinev1.CurrentStatus{
+							Phase: MachineRunning,
+						},
+					},
+				}
+
+				objects := []runtime.Object{}
+				objects = append(objects, testMachineSet, testActiveMachine1, testActiveMachine2, testActiveMachine3)
+				c, trackers := createController(stop, testNamespace, objects, nil, nil)
+				defer trackers.Stop()
+				waitForCacheSync(stop, c)
+
+				machines, _ := c.controlMachineClient.Machines(testNamespace).List(context.TODO(), metav1.ListOptions{})
+				Expect(len(machines.Items)).To(Equal(int(testMachineSet.Spec.Replicas)))
+
+				activeMachines := []*machinev1.Machine{testActiveMachine1, testActiveMachine2, testActiveMachine3}
+				Err := c.manageReplicas(context.TODO(), activeMachines, testMachineSet)
+				waitForCacheSync(stop, c)
+				machines, _ = c.controlMachineClient.Machines(testNamespace).List(context.TODO(), metav1.ListOptions{})
+				Expect(len(machines.Items)).To(Equal(int(testMachineSet.Spec.Replicas)))
+				Expect(Err).Should(BeNil())
+			})
+
+			// TestCase: ActiveMachines + MachinesWithUpdateSuccessfulLabel > DesiredMachines
+			It("should not return error and delete extra running machine", func() {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				testActiveMachine3 = &machinev1.Machine{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "machine-3",
+						Namespace: testNamespace,
+						UID:       "12345610",
+						Labels: map[string]string{
+							"test-label":                             "test-label",
+							"node.machine.sapcloud.io/update-result": "successful",
+						},
+					},
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Machine",
+						APIVersion: "machine.sapcloud.io/v1alpha1",
+					},
+					Status: machinev1.MachineStatus{
+						CurrentStatus: machinev1.CurrentStatus{
+							Phase: MachineRunning,
+						},
+					},
+				}
+
+				testActiveMachine4 = &machinev1.Machine{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "machine-4",
+						Namespace: testNamespace,
+						UID:       "12345611",
+						Labels: map[string]string{
+							"test-label": "test-label",
+						},
+					},
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Machine",
+						APIVersion: "machine.sapcloud.io/v1alpha1",
+					},
+					Status: machinev1.MachineStatus{
+						CurrentStatus: machinev1.CurrentStatus{
+							Phase: MachineRunning,
+						},
+					},
+				}
+
+				testActiveMachine5 = &machinev1.Machine{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "machine-5",
+						Namespace: testNamespace,
+						UID:       "12345612",
+						Labels: map[string]string{
+							"test-label": "test-label",
+						},
+					},
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Machine",
+						APIVersion: "machine.sapcloud.io/v1alpha1",
+					},
+					Status: machinev1.MachineStatus{
+						CurrentStatus: machinev1.CurrentStatus{
+							Phase: MachineRunning,
+						},
+					},
+				}
+
+				objects := []runtime.Object{}
+				objects = append(objects, testMachineSet, testActiveMachine1, testActiveMachine2, testActiveMachine3, testActiveMachine4, testActiveMachine5)
+				c, trackers := createController(stop, testNamespace, objects, nil, nil)
+				defer trackers.Stop()
+				waitForCacheSync(stop, c)
+
+				machines, _ := c.controlMachineClient.Machines(testNamespace).List(context.TODO(), metav1.ListOptions{})
+				Expect(len(machines.Items)).To(Equal(int(testMachineSet.Spec.Replicas) + 2))
+
+				activeMachines := []*machinev1.Machine{testActiveMachine1, testActiveMachine2, testActiveMachine3, testActiveMachine4, testActiveMachine5}
+				Err := c.manageReplicas(context.TODO(), activeMachines, testMachineSet)
+				waitForCacheSync(stop, c)
+				machines, _ = c.controlMachineClient.Machines(testNamespace).List(context.TODO(), metav1.ListOptions{})
+				Expect(len(machines.Items)).To(Equal(int(testMachineSet.Spec.Replicas) + 1))
+				Expect(Err).Should(BeNil())
+			})
 		})
 	})
 
diff --git a/pkg/util/labels/labels_test.go b/pkg/util/labels/labels_test.go
index 806f19994..13d1dd22b 100644
--- a/pkg/util/labels/labels_test.go
+++ b/pkg/util/labels/labels_test.go
@@ -214,3 +214,58 @@ func TestAddLabelToSelector(t *testing.T) {
 		}
 	}
 }
+
+func TestGetLabelsAsJSONBytes(t *testing.T) {
+	cases := []struct {
+		labels map[string]string
+		want   string
+	}{
+		{
+			labels: map[string]string{
+				"foo1": "bar1",
+				"foo2": "bar2",
+			},
+			want: `{"foo1":"bar1","foo2":"bar2"}`,
+		},
+		{
+			labels: map[string]string{},
+			want:   "{}", // Expected empty string for no labels
+		},
+	}
+
+	for _, tc := range cases {
+		got, _ := GetLabelsAsJSONBytes(tc.labels)
+		if string(got) != tc.want {
+			t.Errorf("GetLabelsAsJSONBytes(%v) = %q, want %q", tc.labels, got, tc.want)
+		}
+	}
+}
+func TestRemoveLabels(t *testing.T) {
+	cases := []struct {
+		labelsToRemove []string
+		want           string
+	}{
+		{
+			labelsToRemove: []string{"foo1", "foo2"},
+			want:           `{"metadata":{"labels":{"foo1":null,"foo2":null}}}`,
+		},
+		{
+			labelsToRemove: []string{"foo3"},
+			want:           `{"metadata":{"labels":{"foo3":null}}}`,
+		},
+		{
+			labelsToRemove: []string{},
+			want:           `{"metadata":{"labels":{}}}`,
+		},
+	}
+
+	for _, tc := range cases {
+		got, err := RemoveLabels(tc.labelsToRemove)
+		if err != nil {
+			t.Errorf("RemoveLabels(%v) returned error: %v", tc.labelsToRemove, err)
+		}
+		if string(got) != tc.want {
+			t.Errorf("RemoveLabels(%v) = %q, want %q", tc.labelsToRemove, got, tc.want)
+		}
+	}
+}
diff --git a/pkg/util/provider/machinecontroller/machine_test.go b/pkg/util/provider/machinecontroller/machine_test.go
index 493988e87..5ae574701 100644
--- a/pkg/util/provider/machinecontroller/machine_test.go
+++ b/pkg/util/provider/machinecontroller/machine_test.go
@@ -136,6 +136,66 @@ var _ = Describe("machine", func() {
 		)
 	})
 
+	Describe("addedInPlaceUpdateLabels", func() {
+		type testCase struct {
+			oldNode  *corev1.Node
+			node     *corev1.Node
+			expected bool
+		}
+
+		DescribeTable("##table",
+			func(tc testCase) {
+				result := inPlaceUpdateLabelsChanged(tc.oldNode, tc.node)
+				Expect(result).To(Equal(tc.expected))
+			},
+			Entry("both nodes are nil", testCase{
+				oldNode:  nil,
+				node:     nil,
+				expected: false,
+			}),
+			Entry("oldNode is nil", testCase{
+				oldNode:  nil,
+				node:     &corev1.Node{},
+				expected: false,
+			}),
+			Entry("node is nil", testCase{
+				oldNode:  &corev1.Node{},
+				node:     nil,
+				expected: false,
+			}),
+			Entry("no labels added or changed", testCase{
+				oldNode:  &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"someLabel": "someValue"}}},
+				node:     &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"someLabel": "someValue"}}},
+				expected: false,
+			}),
+			Entry("LabelKeyNodeCandidateForUpdate added", testCase{
+				oldNode:  &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
+				node:     &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1alpha1.LabelKeyNodeCandidateForUpdate: "true"}}},
+				expected: true,
+			}),
+			Entry("LabelKeyNodeSelectedForUpdate added", testCase{
+				oldNode:  &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
+				node:     &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1alpha1.LabelKeyNodeSelectedForUpdate: "true"}}},
+				expected: true,
+			}),
+			Entry("LabelKeyNodeUpdateResult added", testCase{
+				oldNode:  &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
+				node:     &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1alpha1.LabelKeyNodeUpdateResult: "success"}}},
+				expected: true,
+			}),
+			Entry("LabelKeyNodeUpdateResult changed", testCase{
+				oldNode:  &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1alpha1.LabelKeyNodeUpdateResult: "failure"}}},
+				node:     &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1alpha1.LabelKeyNodeUpdateResult: "success"}}},
+				expected: true,
+			}),
+			Entry("LabelKeyNodeUpdateResult unchanged", testCase{
+				oldNode:  &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1alpha1.LabelKeyNodeUpdateResult: "success"}}},
+				node:     &corev1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1alpha1.LabelKeyNodeUpdateResult: "success"}}},
+				expected: false,
+			}),
+		)
+	})
+
 	/*
 		Describe("##updateMachineConditions", func() {
 			Describe("Update conditions of a non-existing machine", func() {
@@ -849,7 +909,7 @@ var _ = Describe("machine", func() {
 						},
 					}, nil, nil, nil, true, metav1.Now()),
 					err:   status.Error(codes.ResourceExhausted, "Provider does not have capacity to create VM"),
-					retry: machineutils.MediumRetry,
+					retry: machineutils.LongRetry,
 				},
 			}),
 			Entry("Machine creation fails with Failure due to timeout", &data{
diff --git a/pkg/util/provider/machinecontroller/machine_util_test.go b/pkg/util/provider/machinecontroller/machine_util_test.go
index 4d43b1263..394c76a84 100644
--- a/pkg/util/provider/machinecontroller/machine_util_test.go
+++ b/pkg/util/provider/machinecontroller/machine_util_test.go
@@ -13,6 +13,7 @@ import (
 
 	machinev1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
 	"github.com/gardener/machine-controller-manager/pkg/fakeclient"
+	"github.com/gardener/machine-controller-manager/pkg/util/nodeops"
 	"github.com/gardener/machine-controller-manager/pkg/util/permits"
 	"github.com/gardener/machine-controller-manager/pkg/util/provider/machineutils"
 	. "github.com/onsi/ginkgo/v2"
@@ -22,6 +23,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/klog/v2"
+	"k8s.io/utils/ptr"
 )
 
 const (
@@ -2295,6 +2297,120 @@ var _ = Describe("machine_util", func() {
 					expectedPhase: machinev1.MachinePending,
 				},
 			}),
+			Entry("Machine in Unknown state WITHOUT backing node obj for over 10min(healthTimeout) should not be marked Failed if DisableHealthTimeout is set", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0), Spec: machinev1.MachineSpec{MachineConfiguration: &machinev1.MachineConfiguration{DisableHealthTimeout: ptr.To(true)}}},
+							&machinev1.MachineStatus{Conditions: nodeConditions(false, false, false, false, false), CurrentStatus: machinev1.CurrentStatus{Phase: machinev1.MachineUnknown, LastUpdateTime: metav1.NewTime(time.Now().Add(-15 * time.Minute))}},
+							&metav1.OwnerReference{Name: machineSet1Deploy1},
+							nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod:   machineutils.LongRetry,
+					expectedPhase: machinev1.MachineUnknown,
+				},
+			}),
+			Entry("Machine in InPlaceUpdating state for over 10min(InPlaceUpdateTimeout) should be marked InPlaceUpdateFailed", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							&machinev1.MachineStatus{Conditions: nodeConditions(false, false, false, false, false), CurrentStatus: machinev1.CurrentStatus{Phase: machinev1.MachineInPlaceUpdating, LastUpdateTime: metav1.NewTime(time.Now().Add(-15 * time.Minute))}},
+							&metav1.OwnerReference{Name: machineSet1Deploy1},
+							nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning, Conditions: nodeConditions(false, false, false, false, false)}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod:   machineutils.ShortRetry,
+					err:           errSuccessfulPhaseUpdate,
+					expectedPhase: machinev1.MachineInPlaceUpdateFailed,
+				},
+			}),
+			Entry("Machine in InPlaceUpdating state with node label node.machine.sapcloud.io/update-result=successful should be marked InPlaceUpdateSuccessful", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							&machinev1.MachineStatus{Conditions: nodeConditions(false, false, false, false, false), CurrentStatus: machinev1.CurrentStatus{Phase: machinev1.MachineInPlaceUpdating, LastUpdateTime: metav1.NewTime(time.Now())}},
+							&metav1.OwnerReference{Name: machineSet1Deploy1},
+							nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, map[string]string{machinev1.LabelKeyNodeUpdateResult: machinev1.LabelValueNodeUpdateSuccessful}, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning, Conditions: nodeConditions(false, false, false, false, false)}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod:   machineutils.ShortRetry,
+					err:           errSuccessfulPhaseUpdate,
+					expectedPhase: machinev1.MachineInPlaceUpdateSuccessful,
+				},
+			}),
+			Entry("Machine in InPlaceUpdating state with node label node.machine.sapcloud.io/update-result=failed should be marked InPlaceUpdateFailed", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							&machinev1.MachineStatus{Conditions: nodeConditions(false, false, false, false, false), CurrentStatus: machinev1.CurrentStatus{Phase: machinev1.MachineInPlaceUpdating, LastUpdateTime: metav1.NewTime(time.Now())}},
+							&metav1.OwnerReference{Name: machineSet1Deploy1},
+							nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, map[string]string{machinev1.LabelKeyNodeUpdateResult: machinev1.LabelValueNodeUpdateFailed}, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning, Conditions: nodeConditions(false, false, false, false, false)}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod:   machineutils.ShortRetry,
+					err:           errSuccessfulPhaseUpdate,
+					expectedPhase: machinev1.MachineInPlaceUpdateFailed,
+				},
+			}),
+			Entry("Machine in InPlaceUpdateFailed state for over 10min(healthTimeout) should be marked Failed", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							&machinev1.MachineStatus{Conditions: nodeConditions(false, false, false, false, false), CurrentStatus: machinev1.CurrentStatus{Phase: machinev1.MachineInPlaceUpdateFailed, LastUpdateTime: metav1.NewTime(time.Now().Add(-15 * time.Minute))}},
+							&metav1.OwnerReference{Name: machineSet1Deploy1},
+							nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning, Conditions: nodeConditions(false, false, false, false, false)}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod:   machineutils.ShortRetry,
+					expectedPhase: machinev1.MachineFailed,
+				},
+			}),
+			Entry("Machine in InPlaceUpdateFailed state for over 10min(healthTimeout) should not be marked Failed if DisableHealthTimeout is set", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0), Spec: machinev1.MachineSpec{MachineConfiguration: &machinev1.MachineConfiguration{DisableHealthTimeout: ptr.To(true)}}},
+							&machinev1.MachineStatus{Conditions: nodeConditions(false, false, false, false, false), CurrentStatus: machinev1.CurrentStatus{Phase: machinev1.MachineInPlaceUpdateFailed, LastUpdateTime: metav1.NewTime(time.Now().Add(-15 * time.Minute))}},
+							&metav1.OwnerReference{Name: machineSet1Deploy1},
+							nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning, Conditions: nodeConditions(false, false, false, false, false)}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod:   machineutils.LongRetry,
+					expectedPhase: machinev1.MachineInPlaceUpdateFailed,
+				},
+			}),
 			Entry("pending machine is healthy, but node has `critical-components-not-ready` taint, shouldn't be marked Running", &data{
 				setup: setup{
 					machines: []*machinev1.Machine{
@@ -2711,4 +2827,845 @@ var _ = Describe("machine_util", func() {
 			}),
 		)
 	})
+
+	Describe("#updateNodeConditionBasedOnLabel", func() {
+		type setup struct {
+			machines []*machinev1.Machine
+			nodes    []*corev1.Node
+			// targetMachineName is name of machine for which `reconcileMachineHealth()` needs to be called
+			// Its must this machine in machine list
+			targetMachineName string
+		}
+
+		type expect struct {
+			retryPeriod machineutils.RetryPeriod
+			err         error
+			node        *corev1.Node
+		}
+		type data struct {
+			setup  setup
+			expect expect
+		}
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				controlMachineObjects := []runtime.Object{}
+				targetCoreObjects := []runtime.Object{}
+
+				var targetMachine *machinev1.Machine
+
+				for _, o := range data.setup.machines {
+					controlMachineObjects = append(controlMachineObjects, o)
+					if o.Name == data.setup.targetMachineName {
+						targetMachine = o
+					}
+				}
+
+				for _, o := range data.setup.nodes {
+					targetCoreObjects = append(targetCoreObjects, o)
+				}
+
+				c, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects, nil)
+				defer trackers.Stop()
+
+				c.permitGiver = permits.NewPermitGiver(5*time.Second, 1*time.Second)
+				defer c.permitGiver.Close()
+
+				waitForCacheSync(stop, c)
+
+				retryPeriod, err := c.updateNodeConditionBasedOnLabel(context.TODO(), targetMachine)
+
+				Expect(retryPeriod).To(Equal(data.expect.retryPeriod))
+				if data.expect.err == nil {
+					Expect(err).To(BeNil())
+				} else {
+					Expect(err).To(HaveOccurred())
+					Expect(err).To(Equal(data.expect.err))
+				}
+
+				if data.expect.node != nil {
+					updatedNode, getErr := c.targetCoreClient.CoreV1().Nodes().Get(context.TODO(), getNodeName(targetMachine), metav1.GetOptions{})
+					Expect(getErr).To(BeNil())
+					updatedNodeCondition := nodeops.GetCondition(updatedNode, machinev1.NodeInPlaceUpdate)
+					expectedNodeCondition := nodeops.GetCondition(data.expect.node, machinev1.NodeInPlaceUpdate)
+
+					Expect(updatedNodeCondition.Type).To(Equal(expectedNodeCondition.Type))
+					Expect(updatedNodeCondition.Status).To(Equal(expectedNodeCondition.Status))
+					Expect(updatedNodeCondition.Reason).To(Equal(expectedNodeCondition.Reason))
+					Expect(updatedNodeCondition.Message).To(Equal(expectedNodeCondition.Message))
+				}
+			},
+
+			Entry("when node is not found", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							&machinev1.MachineStatus{CurrentStatus: machinev1.CurrentStatus{Phase: machinev1.MachinePending, LastUpdateTime: metav1.NewTime(time.Now().Add(-25 * time.Minute))}},
+							nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0-0"}, true, metav1.Now()),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node:        nil,
+				},
+			}),
+			Entry("when node has LabelKeyNodeCandidateForUpdate", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							nil,
+							nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, map[string]string{machinev1.LabelKeyNodeCandidateForUpdate: "true"},
+							nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning, Conditions: nodeConditions(true, false, false, false, false)}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+							Labels: map[string]string{
+								machinev1.LabelKeyNodeCandidateForUpdate: "true",
+							},
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.CandidateForUpdate,
+									Message:            "Node is a candidate for in-place update",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node has LabelKeyNodeCandidateForUpdate but still not selected for update", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							nil,
+							nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, map[string]string{
+							machinev1.LabelKeyNodeCandidateForUpdate: "true",
+						},
+							nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning,
+								Conditions: append(nodeConditions(true, false, false, false, false),
+									corev1.NodeCondition{
+										Type:               machinev1.NodeInPlaceUpdate,
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Now(),
+										Reason:             machinev1.CandidateForUpdate,
+										Message:            "Node is a candidate for in-place update"})}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+							Labels: map[string]string{
+								machinev1.LabelKeyNodeCandidateForUpdate: "true",
+							},
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.CandidateForUpdate,
+									Message:            "Node is a candidate for in-place update",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node has LabelKeyNodeCandidateForUpdate also the in-place update reason is UpdateSuccessful", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							nil,
+							nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, map[string]string{
+							machinev1.LabelKeyNodeCandidateForUpdate: "true",
+						},
+							nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning,
+								Conditions: append(nodeConditions(true, false, false, false, false),
+									corev1.NodeCondition{
+										Type:               machinev1.NodeInPlaceUpdate,
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Now(),
+										Reason:             machinev1.UpdateSuccessful,
+										Message:            "Node in-place update successful"})}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+							Labels: map[string]string{
+								machinev1.LabelKeyNodeCandidateForUpdate: "true",
+							},
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.CandidateForUpdate,
+									Message:            "Node is a candidate for in-place update",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node has LabelKeyNodeSelectedForUpdate", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							nil,
+							nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, map[string]string{
+							machinev1.LabelKeyNodeCandidateForUpdate: "true",
+							machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+						},
+							nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning,
+								Conditions: append(nodeConditions(true, false, false, false, false),
+									corev1.NodeCondition{
+										Type:               machinev1.NodeInPlaceUpdate,
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Now(),
+										Reason:             machinev1.CandidateForUpdate,
+										Message:            "Node is a candidate for in-place update"})}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+							Labels: map[string]string{
+								machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+							},
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.SelectedForUpdate,
+									Message:            "Node is selected for in-place update",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node has LabelKeyNodeSelectedForUpdate but drain is not successful yet", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							nil,
+							nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, map[string]string{
+							machinev1.LabelKeyNodeCandidateForUpdate: "true",
+							machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+						},
+							nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning,
+								Conditions: append(nodeConditions(true, false, false, false, false),
+									corev1.NodeCondition{
+										Type:               machinev1.NodeInPlaceUpdate,
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Now(),
+										Reason:             machinev1.SelectedForUpdate,
+										Message:            "Node is selected for in-place update"})}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod: machineutils.MediumRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+							Labels: map[string]string{
+								machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+							},
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.SelectedForUpdate,
+									Message:            "Node is selected for in-place update",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node update is successful", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							nil,
+							nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1, map[string]string{
+							machinev1.LabelKeyNodeCandidateForUpdate: "true",
+							machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+							machinev1.LabelKeyNodeUpdateResult:       "successful",
+						},
+							nil, &corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning,
+								Conditions: append(nodeConditions(true, false, false, false, false),
+									corev1.NodeCondition{
+										Type:               machinev1.NodeInPlaceUpdate,
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Now(),
+										Reason:             machinev1.ReadyForUpdate,
+										Message:            "Node is ready for in-place update"})}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+							Labels: map[string]string{
+								machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+								machinev1.LabelKeyNodeUpdateResult:       "successful",
+							},
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.UpdateSuccessful,
+									Message:            "Node in-place update successful",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node update has failed", &data{
+				setup: setup{
+					machines: []*machinev1.Machine{
+						newMachine(
+							&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+							nil,
+							nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					},
+					nodes: []*corev1.Node{
+						newNode(1,
+							map[string]string{
+								machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+								machinev1.LabelKeyNodeUpdateResult:       "failed",
+							},
+							map[string]string{machinev1.AnnotationKeyMachineUpdateFailedReason: "OS image not found"},
+							&corev1.NodeSpec{}, &corev1.NodeStatus{Phase: corev1.NodeRunning,
+								Conditions: append(nodeConditions(true, false, false, false, false),
+									corev1.NodeCondition{
+										Type:               machinev1.NodeInPlaceUpdate,
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Now(),
+										Reason:             machinev1.DrainSuccessful,
+										Message:            "Node draining successful"})}),
+					},
+					targetMachineName: machineSet1Deploy1 + "-" + "0",
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+							Labels: map[string]string{
+								machinev1.LabelKeyNodeCandidateForUpdate: "true",
+								machinev1.LabelKeyNodeSelectedForUpdate:  "true",
+								machinev1.LabelKeyNodeUpdateResult:       "failed",
+							},
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.UpdateFailed,
+									Message:            fmt.Sprintf("Node in-place update failed: %s", "OS image not found"),
+								},
+							},
+						},
+					},
+				},
+			}),
+		)
+	})
+
+	Describe("#inPlaceUpdate", func() {
+		type setup struct {
+			machine *machinev1.Machine
+			node    *corev1.Node
+		}
+
+		type expect struct {
+			retryPeriod machineutils.RetryPeriod
+			err         error
+			node        *corev1.Node
+		}
+
+		type data struct {
+			setup  setup
+			expect expect
+		}
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				controlMachineObjects := []runtime.Object{}
+				targetCoreObjects := []runtime.Object{}
+
+				controlMachineObjects = append(controlMachineObjects, data.setup.machine)
+				targetCoreObjects = append(targetCoreObjects, data.setup.node)
+
+				c, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects, nil)
+				defer trackers.Stop()
+
+				c.permitGiver = permits.NewPermitGiver(5*time.Second, 1*time.Second)
+				defer c.permitGiver.Close()
+
+				waitForCacheSync(stop, c)
+
+				retryPeriod, err := c.inPlaceUpdate(context.TODO(), data.setup.machine)
+
+				Expect(retryPeriod).To(Equal(data.expect.retryPeriod))
+				if data.expect.err == nil {
+					Expect(err).To(BeNil())
+				} else {
+					Expect(err).To(HaveOccurred())
+					Expect(err.Error()).To(Equal(data.expect.err.Error()))
+				}
+
+				if data.expect.node != nil {
+					updatedNode, getErr := c.targetCoreClient.CoreV1().Nodes().Get(context.TODO(), getNodeName(data.setup.machine), metav1.GetOptions{})
+					Expect(getErr).To(BeNil())
+					updatedNodeCondition := nodeops.GetCondition(updatedNode, machinev1.NodeInPlaceUpdate)
+					expectedNodeCondition := nodeops.GetCondition(data.expect.node, machinev1.NodeInPlaceUpdate)
+
+					Expect(updatedNodeCondition.Type).To(Equal(expectedNodeCondition.Type))
+					Expect(updatedNodeCondition.Status).To(Equal(expectedNodeCondition.Status))
+					Expect(updatedNodeCondition.Reason).To(Equal(expectedNodeCondition.Reason))
+					Expect(updatedNodeCondition.Message).To(Equal(expectedNodeCondition.Message))
+				}
+			},
+
+			Entry("when node is not present", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						nil,
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0-test", machinev1.LabelKeyNodeSelectedForUpdate: "true"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node:        nil,
+				},
+			}),
+			Entry("when node condition is nil", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						nil,
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0", machinev1.LabelKeyNodeSelectedForUpdate: "true"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.LongRetry,
+					err:         nil,
+					node:        nil,
+				},
+			}),
+			Entry("when node condition reason is SelectedForUpdate", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						nil,
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0", machinev1.LabelKeyNodeSelectedForUpdate: "true"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{
+						Conditions: []corev1.NodeCondition{
+							{
+								Type:               machinev1.NodeInPlaceUpdate,
+								Status:             corev1.ConditionTrue,
+								LastTransitionTime: metav1.Now(),
+								Reason:             machinev1.SelectedForUpdate,
+								Message:            "Node is selected for in-place update",
+							},
+						},
+					}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.MediumRetry,
+					err:         fmt.Errorf("node %s is ready for in-place update", "node-0"),
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.ReadyForUpdate,
+									Message:            "Node is ready for in-place update",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node condition reason is DrainSuccessful", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						nil,
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0", machinev1.LabelKeyNodeSelectedForUpdate: "true"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{
+						Conditions: []corev1.NodeCondition{
+							{
+								Type:               machinev1.NodeInPlaceUpdate,
+								Status:             corev1.ConditionTrue,
+								LastTransitionTime: metav1.Now(),
+								Reason:             machinev1.DrainSuccessful,
+								Message:            "Node draining successful",
+							},
+						},
+					}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.MediumRetry,
+					err:         fmt.Errorf("node %s is ready for in-place update", "node-0"),
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.ReadyForUpdate,
+									Message:            "Node is ready for in-place update",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node condition reason is ReadyForUpdate", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						nil,
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0", machinev1.LabelKeyNodeSelectedForUpdate: "true"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{
+						Conditions: []corev1.NodeCondition{
+							{
+								Type:               machinev1.NodeInPlaceUpdate,
+								Status:             corev1.ConditionTrue,
+								LastTransitionTime: metav1.Now(),
+								Reason:             machinev1.ReadyForUpdate,
+								Message:            "Node is ready for in-place update",
+							},
+						},
+					}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.MediumRetry,
+					err:         fmt.Errorf("node %s is ready for in-place update", "node-0"),
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.ReadyForUpdate,
+									Message:            "Node is ready for in-place update",
+								},
+							},
+						},
+					},
+				},
+			}),
+		)
+	})
+
+	Describe("#drainNodeForInPlace", func() {
+		type setup struct {
+			machine *machinev1.Machine
+			node    *corev1.Node
+		}
+
+		type expect struct {
+			retryPeriod machineutils.RetryPeriod
+			err         error
+			node        *corev1.Node
+		}
+
+		type data struct {
+			setup  setup
+			expect expect
+		}
+
+		DescribeTable("##table",
+			func(data *data) {
+				stop := make(chan struct{})
+				defer close(stop)
+
+				controlMachineObjects := []runtime.Object{}
+				targetCoreObjects := []runtime.Object{}
+
+				controlMachineObjects = append(controlMachineObjects, data.setup.machine)
+				if data.setup.node != nil {
+					targetCoreObjects = append(targetCoreObjects, data.setup.node)
+				}
+
+				c, trackers := createController(stop, testNamespace, controlMachineObjects, nil, targetCoreObjects, nil)
+				defer trackers.Stop()
+
+				c.permitGiver = permits.NewPermitGiver(5*time.Second, 1*time.Second)
+				defer c.permitGiver.Close()
+
+				waitForCacheSync(stop, c)
+
+				retryPeriod, err := c.drainNodeForInPlace(context.TODO(), data.setup.machine)
+
+				Expect(retryPeriod).To(Equal(data.expect.retryPeriod))
+				if data.expect.err == nil {
+					Expect(err).To(BeNil())
+				} else {
+					Expect(err).To(HaveOccurred())
+					Expect(err.Error()).To(Equal(data.expect.err.Error()))
+				}
+
+				if data.expect.node != nil {
+					updatedNode, getErr := c.targetCoreClient.CoreV1().Nodes().Get(context.TODO(), getNodeName(data.setup.machine), metav1.GetOptions{})
+					Expect(getErr).To(BeNil())
+					updatedNodeCondition := nodeops.GetCondition(updatedNode, machinev1.NodeInPlaceUpdate)
+					expectedNodeCondition := nodeops.GetCondition(data.expect.node, machinev1.NodeInPlaceUpdate)
+
+					Expect(updatedNodeCondition.Type).To(Equal(expectedNodeCondition.Type))
+					Expect(updatedNodeCondition.Status).To(Equal(expectedNodeCondition.Status))
+					Expect(updatedNodeCondition.Reason).To(Equal(expectedNodeCondition.Reason))
+					Expect(updatedNodeCondition.Message).To(Equal(expectedNodeCondition.Message))
+				}
+			},
+
+			Entry("when node is not found", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						nil,
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0-0"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.ShortRetry,
+					err:         fmt.Errorf("nodes \"node-0-0\" not found"),
+					node:        nil,
+				},
+			}),
+			Entry("when node is not ready for over 5 minutes", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						&machinev1.MachineStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               corev1.NodeReady,
+									Status:             corev1.ConditionFalse,
+									LastTransitionTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
+								},
+							},
+						},
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.ShortRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.DrainSuccessful,
+									Message:            "Node draining successful",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when node is in ReadonlyFilesystem for over 5 minutes", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						&machinev1.MachineStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               "ReadonlyFilesystem",
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
+								},
+							},
+						},
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.ShortRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.DrainSuccessful,
+									Message:            "Node draining successful",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when force drain is triggered", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						nil,
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0", "force-drain": "True"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.ShortRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.DrainSuccessful,
+									Message:            "Node draining successful",
+								},
+							},
+						},
+					},
+				},
+			}),
+			Entry("when normal drain is triggered", &data{
+				setup: setup{
+					machine: newMachine(
+						&machinev1.MachineTemplateSpec{ObjectMeta: *newObjectMeta(&metav1.ObjectMeta{GenerateName: machineSet1Deploy1}, 0)},
+						nil,
+						nil, nil, map[string]string{machinev1.NodeLabelKey: "node-0"}, true, metav1.Now()),
+					node: newNode(1, nil, nil, &corev1.NodeSpec{}, &corev1.NodeStatus{}),
+				},
+				expect: expect{
+					retryPeriod: machineutils.ShortRetry,
+					err:         nil,
+					node: &corev1.Node{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node-0",
+						},
+						Status: corev1.NodeStatus{
+							Conditions: []corev1.NodeCondition{
+								{
+									Type:               machinev1.NodeInPlaceUpdate,
+									Status:             corev1.ConditionTrue,
+									LastTransitionTime: metav1.Now(),
+									Reason:             machinev1.DrainSuccessful,
+									Message:            "Node draining successful",
+								},
+							},
+						},
+					},
+				},
+			}),
+		)
+	})
 })