Repository: Azure/ARO-RP
Pull request #2937 (2023-06-06 21:14:06)

Remove AZURE_ARM_CLIENT_SECRET helper 
### Which issue this PR addresses:

Our local development workflow lays down a role assignment on creation of the cluster resource group to mimic ARM's magical [S2S Resource Group Scoped Authorization](https://armwiki.azurewebsites.net/rbac/authorization/ResourceGroupScopedServicetoServiceAuthorization.html?q=s2s)  

We no longer use client secret in this, so continue using certificate based credentials to lay down this role assignment for development mode.  

### What this PR does / why we need it:

Remove stale/old code.  

### Test plan for issue:

E2e will cover everything

### Is there any documentation that needs to be updated for this PR?
nope


bennerv: Hold as we don't have access to update the SPs in our CI to remove this. 

Patch:
diff --git a/pkg/cluster/clusterserviceprincipal.go b/pkg/cluster/clusterserviceprincipal.go
index 5dde18c33..30ff6b211 100644
--- a/pkg/cluster/clusterserviceprincipal.go
+++ b/pkg/cluster/clusterserviceprincipal.go
@@ -10,14 +10,11 @@ import (
 
 	mgmtauthorization "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-09-01-preview/authorization"
 	"github.com/ghodss/yaml"
-	corev1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	applyv1 "k8s.io/client-go/applyconfigurations/core/v1"
 	"k8s.io/client-go/util/retry"
 
 	"github.com/Azure/ARO-RP/pkg/util/arm"
-	"github.com/Azure/ARO-RP/pkg/util/clusterauthorizer"
 	"github.com/Azure/ARO-RP/pkg/util/rbac"
 	"github.com/Azure/ARO-RP/pkg/util/stringutils"
 )
@@ -183,10 +180,8 @@ func (m *manager) updateOpenShiftSecret(ctx context.Context) error {
 		// azure_client_id: secret_id
 		// azure_client_secret: secret_value
 		// azure_tenant_id: tenant_id
-		secret, err := m.kubernetescli.CoreV1().Secrets(clusterauthorizer.AzureCredentialSecretNameSpace).Get(ctx, clusterauthorizer.AzureCredentialSecretName, metav1.GetOptions{})
-		if kerrors.IsNotFound(err) {
-			secret = m.newAzureCredentialSecret()
-		} else if err != nil {
+		secret, err := m.kubernetescli.CoreV1().Secrets("kube-system").Get(ctx, "azure-credentials", metav1.GetOptions{})
+		if err != nil {
 			return err
 		}
 
@@ -206,8 +201,7 @@ func (m *manager) updateOpenShiftSecret(ctx context.Context) error {
 		}
 
 		if changed {
-			secretApplyConfig := applyv1.Secret(secret.Name, secret.Namespace).WithData(secret.Data)
-			_, err = m.kubernetescli.CoreV1().Secrets(clusterauthorizer.AzureCredentialSecretNameSpace).Apply(ctx, secretApplyConfig, metav1.ApplyOptions{FieldManager: "aro-rp"})
+			_, err = m.kubernetescli.CoreV1().Secrets("kube-system").Update(ctx, secret, metav1.UpdateOptions{})
 			if err != nil {
 				return err
 			}
@@ -235,24 +229,3 @@ func (m *manager) updateOpenShiftSecret(ctx context.Context) error {
 	}
 	return nil
 }
-
-func (m *manager) newAzureCredentialSecret() *corev1.Secret {
-	resourceGroupID := m.doc.OpenShiftCluster.Properties.ClusterProfile.ResourceGroupID
-	return &corev1.Secret{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      clusterauthorizer.AzureCredentialSecretName,
-			Namespace: clusterauthorizer.AzureCredentialSecretNameSpace,
-		},
-		Type: corev1.SecretTypeOpaque,
-		Data: map[string][]byte{
-
-			"azure_subscription_id": []byte(m.subscriptionDoc.ID),
-			"azure_resource_prefix": []byte(m.doc.OpenShiftCluster.Properties.InfraID),
-			"azure_resourcegroup":   []byte(resourceGroupID[strings.LastIndex(resourceGroupID, "/")+1:]),
-			"azure_region":          []byte(m.doc.OpenShiftCluster.Location),
-			"azure_client_id":       []byte(""),
-			"azure_client_secret":   []byte(""),
-			"azure_tenant_id":       []byte(""),
-		},
-	}
-}
diff --git a/pkg/cluster/install.go b/pkg/cluster/install.go
index 16d53de2a..9db879f07 100644
--- a/pkg/cluster/install.go
+++ b/pkg/cluster/install.go
@@ -46,7 +46,11 @@ func (m *manager) adminUpdate() []steps.Step {
 		steps.Action(m.initializeKubernetesClients), // must be first
 		steps.Action(m.ensureBillingRecord),         // belt and braces
 		steps.Action(m.ensureDefaults),
-		steps.Action(m.fixupClusterSPObjectID),
+
+		// TODO: this relies on an authorizer that isn't exposed in the manager
+		// struct, so we'll rebuild the fpAuthorizer and use the error catching
+		// to advance
+		steps.AuthorizationRetryingAction(m.fpAuthorizer, m.fixupClusterSPObjectID),
 		steps.Action(m.fixInfraID), // Old clusters lacks infraID in the database. Which makes code prone to errors.
 	}
 
@@ -162,11 +166,15 @@ func (m *manager) clusterWasCreatedByHive() bool {
 
 func (m *manager) Update(ctx context.Context) error {
 	s := []steps.Step{
-		steps.Action(m.validateResources),
+		steps.AuthorizationRetryingAction(m.fpAuthorizer, m.validateResources),
 		steps.Action(m.initializeKubernetesClients), // All init steps are first
 		steps.Action(m.initializeOperatorDeployer),  // depends on kube clients
 		steps.Action(m.initializeClusterSPClients),
-		steps.Action(m.clusterSPObjectID),
+
+		// TODO: this relies on an authorizer that isn't exposed in the manager
+		// struct, so we'll rebuild the fpAuthorizer and use the error catching
+		// to advance
+		steps.AuthorizationRetryingAction(m.fpAuthorizer, m.clusterSPObjectID),
 		// credentials rotation flow steps
 		steps.Action(m.createOrUpdateClusterServicePrincipalRBAC),
 		steps.Action(m.createOrUpdateDenyAssignment),
@@ -241,7 +249,11 @@ func (m *manager) bootstrap() []steps.Step {
 
 		steps.Action(m.createDNS),
 		steps.Action(m.initializeClusterSPClients), // must run before clusterSPObjectID
-		steps.Action(m.clusterSPObjectID),
+
+		// TODO: this relies on an authorizer that isn't exposed in the manager
+		// struct, so we'll rebuild the fpAuthorizer and use the error catching
+		// to advance
+		steps.AuthorizationRetryingAction(m.fpAuthorizer, m.clusterSPObjectID),
 		steps.Action(m.ensureResourceGroup),
 		steps.Action(m.ensureServiceEndpoints),
 		steps.Action(m.setMasterSubnetPolicies),
diff --git a/pkg/cluster/serviceprincipal.go b/pkg/cluster/serviceprincipal.go
index 0b6297861..354da581b 100644
--- a/pkg/cluster/serviceprincipal.go
+++ b/pkg/cluster/serviceprincipal.go
@@ -51,6 +51,8 @@ func (m *manager) clusterSPObjectID(ctx context.Context) error {
 		var res azgraphrbac.ServicePrincipalObjectResult
 		res, err = m.spApplications.GetServicePrincipalsIDByAppID(ctx, spp.ClientID)
 		if err != nil {
+			// TODO - with migration to MSGraph, this error is different now.
+			// it's fixed with a refreshing action, but we should fix it here too
 			if strings.Contains(err.Error(), "Authorization_IdentityNotFound") {
 				m.log.Info(err)
 				return false, nil
diff --git a/pkg/env/armhelper.go b/pkg/env/armhelper.go
index 4bd5cb628..4af61cd3b 100644
--- a/pkg/env/armhelper.go
+++ b/pkg/env/armhelper.go
@@ -75,30 +75,15 @@ func newARMHelper(ctx context.Context, log *logrus.Entry, env Interface) (ARMHel
 	var tokenCredential azcore.TokenCredential
 	var err error
 
-	if os.Getenv("AZURE_ARM_CLIENT_SECRET") != "" {
-		// TODO: migrate away from AZURE_ARM_CLIENT_SECRET and remove this code
-		// path
-
-		tokenCredential, err = azidentity.NewClientSecretCredential(
-			env.TenantID(),
-			os.Getenv("AZURE_ARM_CLIENT_ID"),
-			os.Getenv("AZURE_ARM_CLIENT_SECRET"),
-			env.Environment().ClientSecretCredentialOptions())
-		if err != nil {
-			return nil, err
-		}
-	} else {
-		key, certs, err := env.ServiceKeyvault().GetCertificateSecret(ctx, RPDevARMSecretName)
-		if err != nil {
-			return nil, err
-		}
+	key, certs, err := env.ServiceKeyvault().GetCertificateSecret(ctx, RPDevARMSecretName)
+	if err != nil {
+		return nil, err
+	}
 
-		options := env.Environment().ClientCertificateCredentialOptions()
-		tokenCredential, err = azidentity.NewClientCertificateCredential(
-			env.TenantID(), os.Getenv("AZURE_ARM_CLIENT_ID"), certs, key, options)
-		if err != nil {
-			return nil, err
-		}
+	options := env.Environment().ClientCertificateCredentialOptions()
+	tokenCredential, err = azidentity.NewClientCertificateCredential(env.TenantID(), os.Getenv("AZURE_ARM_CLIENT_ID"), certs, key, options)
+	if err != nil {
+		return nil, err
 	}
 
 	scopes := []string{env.Environment().ResourceManagerScope}
diff --git a/pkg/frontend/admin_openshiftcluster_vmresize.go b/pkg/frontend/admin_openshiftcluster_vmresize.go
index 33b1cb2b5..3f2270a69 100644
--- a/pkg/frontend/admin_openshiftcluster_vmresize.go
+++ b/pkg/frontend/admin_openshiftcluster_vmresize.go
@@ -5,16 +5,13 @@ package frontend
 
 import (
 	"context"
-	"encoding/json"
 	"net/http"
 	"path/filepath"
+	"regexp"
 	"strings"
 
 	"github.com/go-chi/chi/v5"
 	"github.com/sirupsen/logrus"
-	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	kruntime "k8s.io/apimachinery/pkg/runtime"
 
 	"github.com/Azure/ARO-RP/pkg/api"
 	"github.com/Azure/ARO-RP/pkg/frontend/middleware"
@@ -34,7 +31,7 @@ func (f *frontend) _postAdminOpenShiftClusterVMResize(log *logrus.Entry, ctx con
 	resourceType := chi.URLParam(r, "resourceType")
 	resourceGroupName := chi.URLParam(r, "resourceGroupName")
 
-	action, doc, err := f.prepareAdminActions(log, ctx, vmName, strings.TrimPrefix(r.URL.Path, "/admin"), resourceType, resourceName, resourceGroupName)
+	action, _, err := f.prepareAdminActions(log, ctx, vmName, strings.TrimPrefix(r.URL.Path, "/admin"), resourceType, resourceName, resourceGroupName)
 	if err != nil {
 		return err
 	}
@@ -45,44 +42,30 @@ func (f *frontend) _postAdminOpenShiftClusterVMResize(log *logrus.Entry, ctx con
 		return err
 	}
 
-	k, err := f.kubeActionsFactory(log, f.env, doc.OpenShiftCluster)
+	// checks if the Virtual machines exists in the Cluster RG
+	exists, err := action.ResourceGroupHasVM(ctx, vmName)
 	if err != nil {
 		return err
 	}
-
-	nodeList, err := k.KubeList(ctx, "node", "")
-	if err != nil {
-		return err
-	}
-
-	var u unstructured.Unstructured
-	var nodes corev1.NodeList
-	if err = json.Unmarshal(nodeList, &u); err != nil {
-		return err
-	}
-
-	err = kruntime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &nodes)
-	if err != nil {
-		return err
-	}
-
-	nodeExists := false
-	for _, node := range nodes.Items {
-		if _, ok := node.ObjectMeta.Labels["node-role.kubernetes.io/master"]; !ok {
-			continue
-		}
-
-		if strings.EqualFold(vmName, node.ObjectMeta.Name) {
-			nodeExists = true
-			break
-		}
-	}
-
-	if !nodeExists {
+	if !exists {
 		return api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeNotFound, "",
-			`"The master node '%s' under resource group '%s' was not found."`,
+			`"The VirtualMachine '%s' under resource group '%s' was not found."`,
 			vmName, resourceGroupName)
 	}
 
+	if !nodeIsMaster(vmName) {
+		return api.NewCloudError(http.StatusForbidden, api.CloudErrorCodeForbidden, "",
+			`"The vmName '%s' provided cannot be resized. It is either not a master node or not adhering to the standard naming convention."`,
+			vmName)
+	}
+
 	return action.VMResize(ctx, vmName, vmSize)
 }
+
+// A bland check, to validate if the node is master by checking vmName ends with pattern "-master-[0-9]"
+// return false, when the node is either not a master node or not adhering to the standard naming convention.
+// return true, if regexp satisfies
+func nodeIsMaster(vmName string) bool {
+	r := regexp.MustCompile(`.*-master-[0-9]{1,}$`)
+	return r.MatchString(vmName)
+}
diff --git a/pkg/frontend/adminactions/azureactions.go b/pkg/frontend/adminactions/azureactions.go
index 9497296b9..fa4417cf2 100644
--- a/pkg/frontend/adminactions/azureactions.go
+++ b/pkg/frontend/adminactions/azureactions.go
@@ -35,6 +35,7 @@ type AzureActions interface {
 	VMStopAndWait(ctx context.Context, vmName string, deallocateVM bool) error
 	VMSizeList(ctx context.Context) ([]mgmtcompute.ResourceSku, error)
 	VMResize(ctx context.Context, vmName string, vmSize string) error
+	ResourceGroupHasVM(ctx context.Context, vmName string) (bool, error)
 	VMSerialConsole(ctx context.Context, w http.ResponseWriter, log *logrus.Entry, vmName string) error
 	AppLensGetDetector(ctx context.Context, detectorId string) ([]byte, error)
 	AppLensListDetectors(ctx context.Context) ([]byte, error)
@@ -131,6 +132,22 @@ func (a *azureActions) VMResize(ctx context.Context, vmName string, size string)
 	return a.virtualMachines.CreateOrUpdateAndWait(ctx, clusterRGName, vmName, vm)
 }
 
+func (a *azureActions) ResourceGroupHasVM(ctx context.Context, vmName string) (bool, error) {
+	clusterRGName := stringutils.LastTokenByte(a.oc.Properties.ClusterProfile.ResourceGroupID, '/')
+	vmList, err := a.virtualMachines.List(ctx, clusterRGName)
+	if err != nil {
+		return false, err
+	}
+
+	for _, vm := range vmList {
+		if vm.Name != nil && *vm.Name == vmName {
+			return true, nil
+		}
+	}
+
+	return false, nil
+}
+
 func (a *azureActions) AppLensGetDetector(ctx context.Context, detectorId string) ([]byte, error) {
 	detector, err := a.appLens.GetDetector(ctx, &applens.GetDetectorOptions{ResourceID: a.oc.ID, DetectorID: detectorId, Location: a.oc.Location})
 
diff --git a/pkg/util/mocks/adminactions/adminactions.go b/pkg/util/mocks/adminactions/adminactions.go
index f2bf41227..6dcd15f57 100644
--- a/pkg/util/mocks/adminactions/adminactions.go
+++ b/pkg/util/mocks/adminactions/adminactions.go
@@ -281,6 +281,21 @@ func (mr *MockAzureActionsMockRecorder) NICReconcileFailedState(arg0, arg1 inter
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NICReconcileFailedState", reflect.TypeOf((*MockAzureActions)(nil).NICReconcileFailedState), arg0, arg1)
 }
 
+// ResourceGroupHasVM mocks base method.
+func (m *MockAzureActions) ResourceGroupHasVM(arg0 context.Context, arg1 string) (bool, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ResourceGroupHasVM", arg0, arg1)
+	ret0, _ := ret[0].(bool)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ResourceGroupHasVM indicates an expected call of ResourceGroupHasVM.
+func (mr *MockAzureActionsMockRecorder) ResourceGroupHasVM(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceGroupHasVM", reflect.TypeOf((*MockAzureActions)(nil).ResourceGroupHasVM), arg0, arg1)
+}
+
 // ResourcesList mocks base method.
 func (m *MockAzureActions) ResourcesList(arg0 context.Context, arg1 []features.GenericResourceExpanded, arg2 io.WriteCloser) error {
 	m.ctrl.T.Helper()


Test patch:
diff --git a/pkg/cluster/adminupdate_test.go b/pkg/cluster/adminupdate_test.go
index e07207766..557f23bcd 100644
--- a/pkg/cluster/adminupdate_test.go
+++ b/pkg/cluster/adminupdate_test.go
@@ -43,7 +43,7 @@ func TestAdminUpdateSteps(t *testing.T) {
 				"[Action initializeKubernetesClients-fm]",
 				"[Action ensureBillingRecord-fm]",
 				"[Action ensureDefaults-fm]",
-				"[Action fixupClusterSPObjectID-fm]",
+				"[AuthorizationRetryingAction fixupClusterSPObjectID-fm]",
 				"[Action fixInfraID-fm]",
 				"[Action startVMs-fm]",
 				"[Condition apiServersReady-fm, timeout 30m0s]",
@@ -65,7 +65,7 @@ func TestAdminUpdateSteps(t *testing.T) {
 				"[Action initializeKubernetesClients-fm]",
 				"[Action ensureBillingRecord-fm]",
 				"[Action ensureDefaults-fm]",
-				"[Action fixupClusterSPObjectID-fm]",
+				"[AuthorizationRetryingAction fixupClusterSPObjectID-fm]",
 				"[Action fixInfraID-fm]",
 				"[Action ensureResourceGroup-fm]",
 				"[Action createOrUpdateDenyAssignment-fm]",
@@ -110,7 +110,7 @@ func TestAdminUpdateSteps(t *testing.T) {
 				"[Action initializeKubernetesClients-fm]",
 				"[Action ensureBillingRecord-fm]",
 				"[Action ensureDefaults-fm]",
-				"[Action fixupClusterSPObjectID-fm]",
+				"[AuthorizationRetryingAction fixupClusterSPObjectID-fm]",
 				"[Action fixInfraID-fm]",
 				"[Action ensureResourceGroup-fm]",
 				"[Action createOrUpdateDenyAssignment-fm]",
@@ -151,7 +151,7 @@ func TestAdminUpdateSteps(t *testing.T) {
 				"[Action initializeKubernetesClients-fm]",
 				"[Action ensureBillingRecord-fm]",
 				"[Action ensureDefaults-fm]",
-				"[Action fixupClusterSPObjectID-fm]",
+				"[AuthorizationRetryingAction fixupClusterSPObjectID-fm]",
 				"[Action fixInfraID-fm]",
 				"[Action ensureResourceGroup-fm]",
 				"[Action createOrUpdateDenyAssignment-fm]",
@@ -196,7 +196,7 @@ func TestAdminUpdateSteps(t *testing.T) {
 				"[Action initializeKubernetesClients-fm]",
 				"[Action ensureBillingRecord-fm]",
 				"[Action ensureDefaults-fm]",
-				"[Action fixupClusterSPObjectID-fm]",
+				"[AuthorizationRetryingAction fixupClusterSPObjectID-fm]",
 				"[Action fixInfraID-fm]",
 				"[Action populateDatabaseIntIP-fm]",
 				"[Action startVMs-fm]",
@@ -223,7 +223,7 @@ func TestAdminUpdateSteps(t *testing.T) {
 				"[Action initializeKubernetesClients-fm]",
 				"[Action ensureBillingRecord-fm]",
 				"[Action ensureDefaults-fm]",
-				"[Action fixupClusterSPObjectID-fm]",
+				"[AuthorizationRetryingAction fixupClusterSPObjectID-fm]",
 				"[Action fixInfraID-fm]",
 				"[Action ensureResourceGroup-fm]",
 				"[Action createOrUpdateDenyAssignment-fm]",
diff --git a/pkg/cluster/clusterserviceprincipal_test.go b/pkg/cluster/clusterserviceprincipal_test.go
index 5ed9fa7ad..d3ffe6ee9 100644
--- a/pkg/cluster/clusterserviceprincipal_test.go
+++ b/pkg/cluster/clusterserviceprincipal_test.go
@@ -18,12 +18,8 @@ import (
 	operatorfake "github.com/openshift/client-go/operator/clientset/versioned/fake"
 	"github.com/sirupsen/logrus"
 	corev1 "k8s.io/api/core/v1"
-	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/kubernetes/fake"
-	ktesting "k8s.io/client-go/testing"
 
 	"github.com/Azure/ARO-RP/pkg/api"
 	"github.com/Azure/ARO-RP/pkg/util/arm"
@@ -331,7 +327,7 @@ func TestUpdateOpenShiftSecret(t *testing.T) {
 			name: "noop",
 			kubernetescli: func() *fake.Clientset {
 				secret := getFakeOpenShiftSecret()
-				return cliWithApply(&secret)
+				return fake.NewSimpleClientset(&secret)
 			},
 			doc: api.OpenShiftCluster{
 				Properties: api.OpenShiftClusterProperties{
@@ -356,7 +352,7 @@ func TestUpdateOpenShiftSecret(t *testing.T) {
 			name: "update secret",
 			kubernetescli: func() *fake.Clientset {
 				secret := getFakeOpenShiftSecret()
-				return cliWithApply(&secret)
+				return fake.NewSimpleClientset(&secret)
 			},
 			doc: api.OpenShiftCluster{
 				Properties: api.OpenShiftClusterProperties{
@@ -383,7 +379,7 @@ func TestUpdateOpenShiftSecret(t *testing.T) {
 			name: "update tenant",
 			kubernetescli: func() *fake.Clientset {
 				secret := getFakeOpenShiftSecret()
-				return cliWithApply(&secret)
+				return fake.NewSimpleClientset(&secret)
 			},
 			doc: api.OpenShiftCluster{
 				Properties: api.OpenShiftClusterProperties{
@@ -407,39 +403,16 @@ func TestUpdateOpenShiftSecret(t *testing.T) {
 			},
 		},
 		{
-			name: "recreate secret when not found",
+			name: "not found - no fail",
 			kubernetescli: func() *fake.Clientset {
-				return cliWithApply()
+				return fake.NewSimpleClientset()
 			},
 			doc: api.OpenShiftCluster{
-				Location: "azure_region_value",
 				Properties: api.OpenShiftClusterProperties{
-					ServicePrincipalProfile: api.ServicePrincipalProfile{
-						ClientID:     "azure_client_id_value",
-						ClientSecret: "azure_client_secret_value",
-					},
-					InfraID: "azure_resource_prefix_value",
-					ClusterProfile: api.ClusterProfile{
-						ResourceGroupID: "fake/azure/paths/azure_resourcegroup_value",
-					},
+					ServicePrincipalProfile: api.ServicePrincipalProfile{},
 				},
 			},
-			subscriptionDoc: api.SubscriptionDocument{
-				ID: "azure_subscription_id_value",
-				Subscription: &api.Subscription{
-					Properties: &api.SubscriptionProperties{
-						TenantID: "azure_tenant_id_value",
-					},
-				},
-			},
-			expect: func() corev1.Secret {
-				secret := getFakeOpenShiftSecret()
-				secret.Data["azure_region"] = []byte("azure_region_value")
-				secret.Data["azure_resource_prefix"] = []byte("azure_resource_prefix_value")
-				secret.Data["azure_resourcegroup"] = []byte("azure_resourcegroup_value")
-				secret.Data["azure_subscription_id"] = []byte("azure_subscription_id_value")
-				return secret
-			},
+			wantErr: `secrets "azure-credentials" not found`,
 		},
 	} {
 		t.Run(tt.name, func(t *testing.T) {
@@ -465,44 +438,3 @@ func TestUpdateOpenShiftSecret(t *testing.T) {
 		})
 	}
 }
-
-// The current kubernetes testing client does not propery handle Apply actions so we reimplement it here.
-// See https://github.com/kubernetes/client-go/issues/1184 for more details.
-func cliWithApply(object ...runtime.Object) *fake.Clientset {
-	fc := fake.NewSimpleClientset(object...)
-	fc.PrependReactor("patch", "secrets", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
-		pa := action.(ktesting.PatchAction)
-		if pa.GetPatchType() == types.ApplyPatchType {
-			// Apply patches are supposed to upsert, but fake client fails if the object doesn't exist,
-			// if an apply patch occurs for a secret that doesn't yet exist, create it.
-			// However, we already hold the fakeclient lock, so we can't use the front door.
-			rfunc := ktesting.ObjectReaction(fc.Tracker())
-			_, obj, err := rfunc(
-				ktesting.NewGetAction(pa.GetResource(), pa.GetNamespace(), pa.GetName()),
-			)
-			if kerrors.IsNotFound(err) || obj == nil {
-				_, _, _ = rfunc(
-					ktesting.NewCreateAction(
-						pa.GetResource(),
-						pa.GetNamespace(),
-						&corev1.Secret{
-							ObjectMeta: metav1.ObjectMeta{
-								Name:      pa.GetName(),
-								Namespace: pa.GetNamespace(),
-							},
-						},
-					),
-				)
-			}
-			return rfunc(ktesting.NewPatchAction(
-				pa.GetResource(),
-				pa.GetNamespace(),
-				pa.GetName(),
-				types.StrategicMergePatchType,
-				pa.GetPatch()))
-		}
-		return false, nil, nil
-	},
-	)
-	return fc
-}
diff --git a/pkg/frontend/admin_openshiftcluster_vmresize_test.go b/pkg/frontend/admin_openshiftcluster_vmresize_test.go
index 4aa9b5977..98e715118 100644
--- a/pkg/frontend/admin_openshiftcluster_vmresize_test.go
+++ b/pkg/frontend/admin_openshiftcluster_vmresize_test.go
@@ -5,7 +5,6 @@ package frontend
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
 	"net/http"
 	"strings"
@@ -13,8 +12,6 @@ import (
 
 	"github.com/golang/mock/gomock"
 	"github.com/sirupsen/logrus"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	"github.com/Azure/ARO-RP/pkg/api"
 	"github.com/Azure/ARO-RP/pkg/env"
@@ -35,7 +32,7 @@ func TestAdminVMResize(t *testing.T) {
 		vmName         string
 		vmSize         string
 		fixture        func(f *testdatabase.Fixture)
-		mocks          func(*test, *mock_adminactions.MockAzureActions, *mock_adminactions.MockKubeActions)
+		mocks          func(*test, *mock_adminactions.MockAzureActions)
 		wantStatusCode int
 		wantResponse   []byte
 		wantError      string
@@ -44,7 +41,7 @@ func TestAdminVMResize(t *testing.T) {
 	for _, tt := range []*test{
 		{
 			name:       "basic coverage",
-			vmName:     "aro-fake-node-0",
+			vmName:     "aro-fake-node-master-0",
 			vmSize:     "Standard_D8s_v3",
 			resourceID: testdatabase.GetResourcePath(mockSubID, "resourceName"),
 			fixture: func(f *testdatabase.Fixture) {
@@ -69,34 +66,15 @@ func TestAdminVMResize(t *testing.T) {
 					},
 				})
 			},
-			mocks: func(tt *test, a *mock_adminactions.MockAzureActions, k *mock_adminactions.MockKubeActions) {
-				node := corev1.Node{
-					TypeMeta: metav1.TypeMeta{
-						Kind: "node",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "aro-fake-node-0",
-
-						Labels: map[string]string{
-							"node-role.kubernetes.io/master": "true",
-						},
-					},
-				}
-				nodeList := corev1.NodeList{
-					TypeMeta: metav1.TypeMeta{
-						Kind: "List",
-					},
-					Items: []corev1.Node{node},
-				}
-				marsh, _ := json.Marshal(nodeList)
-				k.EXPECT().KubeList(gomock.Any(), "node", "").Return(marsh, nil)
+			mocks: func(tt *test, a *mock_adminactions.MockAzureActions) {
+				a.EXPECT().ResourceGroupHasVM(gomock.Any(), tt.vmName).Return(true, nil)
 				a.EXPECT().VMResize(gomock.Any(), tt.vmName, tt.vmSize).Return(nil)
 			},
 			wantStatusCode: http.StatusOK,
 		},
 		{
 			name:       "cluster not found",
-			vmName:     "aro-fake-node-0",
+			vmName:     "aro-fake-node-master-0",
 			vmSize:     "Standard_D8s_v3",
 			resourceID: testdatabase.GetResourcePath(mockSubID, "resourceName"),
 			fixture: func(f *testdatabase.Fixture) {
@@ -110,13 +88,13 @@ func TestAdminVMResize(t *testing.T) {
 					},
 				})
 			},
-			mocks:          func(tt *test, a *mock_adminactions.MockAzureActions, k *mock_adminactions.MockKubeActions) {},
+			mocks:          func(tt *test, a *mock_adminactions.MockAzureActions) {},
 			wantStatusCode: http.StatusNotFound,
 			wantError:      `404: ResourceNotFound: : The Resource 'openshiftclusters/resourcename' under resource group 'resourcegroup' was not found.`,
 		},
 		{
 			name:       "subscription doc not found",
-			vmName:     "aro-fake-node-0",
+			vmName:     "aro-fake-node-master-0",
 			vmSize:     "Standard_D8s_v3",
 			resourceID: testdatabase.GetResourcePath(mockSubID, "resourceName"),
 			fixture: func(f *testdatabase.Fixture) {
@@ -132,13 +110,13 @@ func TestAdminVMResize(t *testing.T) {
 					},
 				})
 			},
-			mocks:          func(tt *test, a *mock_adminactions.MockAzureActions, k *mock_adminactions.MockKubeActions) {},
+			mocks:          func(tt *test, a *mock_adminactions.MockAzureActions) {},
 			wantStatusCode: http.StatusBadRequest,
 			wantError:      fmt.Sprintf(`400: InvalidSubscriptionState: : Request is not allowed in unregistered subscription '%s'.`, mockSubID),
 		},
 		{
 			name:       "master node not found",
-			vmName:     "aro-fake-node-0",
+			vmName:     "aro-fake-node-master-0",
 			vmSize:     "Standard_D8s_v3",
 			resourceID: testdatabase.GetResourcePath(mockSubID, "resourceName"),
 			fixture: func(f *testdatabase.Fixture) {
@@ -163,37 +141,51 @@ func TestAdminVMResize(t *testing.T) {
 					},
 				})
 			},
-			mocks: func(tt *test, a *mock_adminactions.MockAzureActions, k *mock_adminactions.MockKubeActions) {
-				node := corev1.Node{
-					TypeMeta: metav1.TypeMeta{
-						Kind: "node",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "aro-fake-node-0",
-
-						Labels: map[string]string{
-							"node-role.kubernetes.io/worker": "true",
+			mocks: func(tt *test, a *mock_adminactions.MockAzureActions) {
+				a.EXPECT().ResourceGroupHasVM(gomock.Any(), tt.vmName).Return(false, nil)
+			},
+			wantStatusCode: http.StatusNotFound,
+			wantError:      `404: NotFound: : "The VirtualMachine 'aro-fake-node-master-0' under resource group 'resourcegroup' was not found."`,
+		},
+		{
+			name:       "node is not master, has not master keyword in it",
+			vmName:     "aro-fake-node-0",
+			vmSize:     "Standard_D8s_v3",
+			resourceID: testdatabase.GetResourcePath(mockSubID, "resourceName"),
+			fixture: func(f *testdatabase.Fixture) {
+				f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
+					Key: strings.ToLower(testdatabase.GetResourcePath(mockSubID, "resourceName")),
+					OpenShiftCluster: &api.OpenShiftCluster{
+						ID: testdatabase.GetResourcePath(mockSubID, "resourceName"),
+						Properties: api.OpenShiftClusterProperties{
+							ClusterProfile: api.ClusterProfile{
+								ResourceGroupID: fmt.Sprintf("/subscriptions/%s/resourceGroups/test-cluster", mockSubID),
+							},
 						},
 					},
-				}
-				nodeList := corev1.NodeList{
-					TypeMeta: metav1.TypeMeta{
-						Kind: "List",
+				})
+				f.AddSubscriptionDocuments(&api.SubscriptionDocument{
+					ID: mockSubID,
+					Subscription: &api.Subscription{
+						State: api.SubscriptionStateRegistered,
+						Properties: &api.SubscriptionProperties{
+							TenantID: mockTenantID,
+						},
 					},
-					Items: []corev1.Node{node},
-				}
-				marsh, _ := json.Marshal(nodeList)
-				k.EXPECT().KubeList(gomock.Any(), "node", "").Return(marsh, nil)
+				})
 			},
-			wantStatusCode: http.StatusNotFound,
-			wantError:      `404: NotFound: : "The master node 'aro-fake-node-0' under resource group 'resourcegroup' was not found."`,
+			mocks: func(tt *test, a *mock_adminactions.MockAzureActions) {
+				a.EXPECT().ResourceGroupHasVM(gomock.Any(), tt.vmName).Return(true, nil)
+			},
+			wantStatusCode: http.StatusForbidden,
+			wantError:      `403: Forbidden: : "The vmName 'aro-fake-node-0' provided cannot be resized. It is either not a master node or not adhering to the standard naming convention."`,
 		},
 		{
 			name:           "invalid vmname",
 			vmName:         "%26&ampersandvmname",
 			resourceID:     testdatabase.GetResourcePath(mockSubID, "resourceName"),
 			vmSize:         "Standard_D8s_v3",
-			mocks:          func(tt *test, a *mock_adminactions.MockAzureActions, k *mock_adminactions.MockKubeActions) {},
+			mocks:          func(tt *test, a *mock_adminactions.MockAzureActions) {},
 			wantStatusCode: http.StatusBadRequest,
 			wantError:      `400: InvalidParameter: : The provided vmName '&' is invalid.`,
 		},
@@ -203,18 +195,15 @@ func TestAdminVMResize(t *testing.T) {
 			defer ti.done()
 
 			a := mock_adminactions.NewMockAzureActions(ti.controller)
-			k := mock_adminactions.NewMockKubeActions(ti.controller)
-			tt.mocks(tt, a, k)
+			tt.mocks(tt, a)
 
 			err := ti.buildFixtures(tt.fixture)
 			if err != nil {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil,
-				func(e *logrus.Entry, i env.Interface, osc *api.OpenShiftCluster) (adminactions.KubeActions, error) {
-					return k, nil
-				}, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil,
+				func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
 					return a, nil
 				}, nil)