diff --git a/azure/scope/identity.go b/azure/scope/identity.go index 75ab60b36f18..b5265e8bba4c 100644 --- a/azure/scope/identity.go +++ b/azure/scope/identity.go @@ -148,7 +148,7 @@ func (p *AzureCredentialsProvider) GetAuthorizer(ctx context.Context, resourceMa var spt *adal.ServicePrincipalToken switch p.Identity.Spec.Type { case infrav1.ServicePrincipal: - if err := createAzureIdentityWithBindings(ctx, p.Identity, clusterMeta, p.Client); err != nil { + if err := createAzureIdentityWithBindings(ctx, p.Identity, resourceManagerEndpoint, activeDirectoryEndpoint, clusterMeta, p.Client); err != nil { return nil, err } @@ -212,7 +212,7 @@ func (p *AzureCredentialsProvider) GetTenantID() string { return p.Identity.Spec.TenantID } -func createAzureIdentityWithBindings(ctx context.Context, azureIdentity *infrav1.AzureClusterIdentity, clusterMeta metav1.ObjectMeta, +func createAzureIdentityWithBindings(ctx context.Context, azureIdentity *infrav1.AzureClusterIdentity, resourceManagerEndpoint, activeDirectoryEndpoint string, clusterMeta metav1.ObjectMeta, kubeClient client.Client) error { azureIdentityType, err := getAzureIdentityType(azureIdentity) if err != nil { @@ -247,6 +247,8 @@ func createAzureIdentityWithBindings(ctx context.Context, azureIdentity *infrav1 ClientID: azureIdentity.Spec.ClientID, ClientPassword: azureIdentity.Spec.ClientSecret, ResourceID: azureIdentity.Spec.ResourceID, + ADResourceID: resourceManagerEndpoint, + ADEndpoint: activeDirectoryEndpoint, }, } err = kubeClient.Create(ctx, copiedIdentity) diff --git a/azure/scope/identity_test.go b/azure/scope/identity_test.go index 1da0fe5de4c3..cbefddb426b3 100644 --- a/azure/scope/identity_test.go +++ b/azure/scope/identity_test.go @@ -20,6 +20,7 @@ import ( "context" "testing" + aadpodv1 "github.com/Azure/aad-pod-identity/pkg/apis/aadpodidentity/v1" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/runtime" @@ -27,6 +28,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -133,3 +135,134 @@ func TestAllowedNamespaces(t *testing.T) { }) } } + +func TestCreateAzureIdentityWithBindings(t *testing.T) { + g := NewWithT(t) + scheme := runtime.NewScheme() + _ = infrav1.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + _ = aadpodv1.AddToScheme(scheme) + + tests := []struct { + name string + identity *infrav1.AzureClusterIdentity + identityType aadpodv1.IdentityType + resourceManagerEndpoint string + activeDirectoryEndpoint string + clusterMeta metav1.ObjectMeta + copiedIdentity metav1.ObjectMeta + binding metav1.ObjectMeta + expectedErr bool + }{ + { + name: "create service principal identity", + identity: &infrav1.AzureClusterIdentity{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-identity", + }, + Spec: infrav1.AzureClusterIdentitySpec{ + Type: infrav1.ServicePrincipal, + ResourceID: "my-resource-id", + ClientID: "my-client-id", + ClientSecret: corev1.SecretReference{Name: "my-client-secret"}, + TenantID: "my-tenant-id", + }, + }, + identityType: aadpodv1.ServicePrincipal, + resourceManagerEndpoint: "public-cloud-endpoint", + activeDirectoryEndpoint: "active-directory-endpoint", + clusterMeta: metav1.ObjectMeta{ + Name: "cluster-name", + Namespace: "my-namespace", + }, + copiedIdentity: metav1.ObjectMeta{ + Name: "cluster-name-my-namespace-test-identity", + Namespace: "capz-system", + }, + binding: metav1.ObjectMeta{ + Name: "cluster-name-my-namespace-test-identity-binding", + Namespace: "capz-system", + }, + }, + { + name: "create UAMI identity", + identity: &infrav1.AzureClusterIdentity{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-identity", + }, + Spec: infrav1.AzureClusterIdentitySpec{ + Type: infrav1.UserAssignedMSI, + ResourceID: "my-resource-id", + ClientID: "my-client-id", + ClientSecret: corev1.SecretReference{Name: "my-client-secret"}, + TenantID: "my-tenant-id", + }, + }, + identityType: aadpodv1.UserAssignedMSI, + resourceManagerEndpoint: "public-cloud-endpoint", + activeDirectoryEndpoint: "active-directory-endpoint", + clusterMeta: metav1.ObjectMeta{ + Name: "cluster-name", + Namespace: "my-namespace", + }, + copiedIdentity: metav1.ObjectMeta{ + Name: "cluster-name-my-namespace-test-identity", + Namespace: "capz-system", + }, + binding: metav1.ObjectMeta{ + Name: "cluster-name-my-namespace-test-identity-binding", + Namespace: "capz-system", + }, + }, + { + name: "invalid identity type", + identity: &infrav1.AzureClusterIdentity{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-identity", + }, + Spec: infrav1.AzureClusterIdentitySpec{ + Type: "fooIdentity", + ResourceID: "my-resource-id", + ClientID: "my-client-id", + ClientSecret: corev1.SecretReference{Name: "my-client-secret"}, + TenantID: "my-tenant-id", + }, + }, + identityType: 0, + expectedErr: true, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + initObjects := []runtime.Object{tc.identity} + fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() + + err := createAzureIdentityWithBindings(context.TODO(), tc.identity, tc.resourceManagerEndpoint, tc.activeDirectoryEndpoint, tc.clusterMeta, fakeClient) + if !tc.expectedErr { + + g.Expect(err).To(BeNil()) + + resultIdentity := &aadpodv1.AzureIdentity{} + key := client.ObjectKey{Name: tc.copiedIdentity.Name, Namespace: tc.copiedIdentity.Namespace} + g.Expect(fakeClient.Get(context.TODO(), key, resultIdentity)).To(Succeed()) + g.Expect(resultIdentity.Spec.Type).To(Equal(tc.identityType)) + g.Expect(resultIdentity.Spec.ResourceID).To(Equal(tc.identity.Spec.ResourceID)) + g.Expect(resultIdentity.Spec.ClientID).To(Equal(tc.identity.Spec.ClientID)) + g.Expect(resultIdentity.Spec.ClientPassword).To(Equal(tc.identity.Spec.ClientSecret)) + g.Expect(resultIdentity.Spec.TenantID).To(Equal(tc.identity.Spec.TenantID)) + g.Expect(resultIdentity.Spec.ADResourceID).To(Equal(tc.resourceManagerEndpoint)) + g.Expect(resultIdentity.Spec.ADEndpoint).To(Equal(tc.activeDirectoryEndpoint)) + + resultIdentityBinding := &aadpodv1.AzureIdentityBinding{} + key = client.ObjectKey{Name: tc.binding.Name, Namespace: tc.binding.Namespace} + g.Expect(fakeClient.Get(context.TODO(), key, resultIdentityBinding)).To(Succeed()) + + // no error if identity already exists + err = createAzureIdentityWithBindings(context.TODO(), tc.identity, tc.resourceManagerEndpoint, tc.activeDirectoryEndpoint, tc.clusterMeta, fakeClient) + g.Expect(err).To(BeNil()) + } else { + g.Expect(err).ToNot(BeNil()) + } + }) + } +}