Search code examples
azureazure-sdk

Creating a service principal with the Azure java sdk fails


I am trying to create a service principal using the azure sdk. However, I am receiving an error

{"odata.error":{"code":"Authorization_RequestDenied","message":{"lang":"en","value":"Insufficient privileges to complete the operation."}}}

What am I doing wrong? I am doing the following:

  1. Create a service principal with the Owner role

    az ad sp create-for-rbac -n "OrbitTest5" --role Owner --sdk-auth
    
  2. Pass the credentials for the created service principal to a credential provider via environment variables

    public class AzureAppEnvCredentialProvider implements AzureCredentialProvider {
      public static final String ENV_CLIENT_ID = "CLIENT_ID";
      public static final String ENV_TENANT_ID = "TENANT_ID";
      public static final String ENV_SUBSCRIPTION_ID = "SUBSCRIPTION_ID";
      public static final String ENV_CLIENT_SECRET = "CLIENT_SECRET";
    
      private final String subscriptionId;
    
      public AzureAppEnvCredentialProvider() {
        this.subscriptionId = Preconditions.checkNotNull(System.getenv(ENV_SUBSCRIPTION_ID));
      }
    
      @Override
      public AzureTokenCredentials getCredentials() {
        final String clientId = Preconditions.checkNotNull(System.getenv(ENV_CLIENT_ID));
        final String tenantId = Preconditions.checkNotNull(System.getenv(ENV_TENANT_ID));
        final String clientSecret = Preconditions.checkNotNull(System.getenv(ENV_CLIENT_SECRET));
        return new ApplicationTokenCredentials(clientId, tenantId, clientSecret, AzureEnvironment.AZURE);
      }
    
      @Override
      public String getSubscriptionId() {
        return this.subscriptionId;
      }
    }
    
  3. Use the credentials to create a service principal with the java sdk

        azureAuthClient = Azure.configure().authenticate(credentialProvider.getCredentials());
    
        final ServicePrincipal servicePrincipal = 
            azureAuthClient.servicePrincipals()
            .define(clusterId)
            .withNewApplication("http://easycreate.azure.com/" + clusterId)
              .definePasswordCredential("sppass")
              .withPasswordValue("StrongPass!12")
              .attach()
            .create();
    
  4. Then I get an exception. I know my credentials are valid because I am able to create a resource group with the sdk and view it from the Azure web console.

    com.microsoft.azure.management.graphrbac.GraphErrorException: Status code 403, {"odata.error":{"code":"Authorization_RequestDenied","message":{"lang":"en","value":"Insufficient privileges to complete the operation."}}} at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at com.microsoft.rest.ServiceResponseBuilder.build(ServiceResponseBuilder.java:122) at com.microsoft.azure.AzureResponseBuilder.build(AzureResponseBuilder.java:56) at com.microsoft.azure.management.graphrbac.implementation.ApplicationsInner.createDelegate(ApplicationsInner.java:194) at com.microsoft.azure.management.graphrbac.implementation.ApplicationsInner.access$000(ApplicationsInner.java:45) at com.microsoft.azure.management.graphrbac.implementation.ApplicationsInner$2.call(ApplicationsInner.java:181) at com.microsoft.azure.management.graphrbac.implementation.ApplicationsInner$2.call(ApplicationsInner.java:177) at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69) at retrofit2.adapter.rxjava.CallArbiter.deliverResponse(CallArbiter.java:120) at retrofit2.adapter.rxjava.CallArbiter.emitResponse(CallArbiter.java:102) at retrofit2.adapter.rxjava.CallExecuteOnSubscribe.call(CallExecuteOnSubscribe.java:46) at retrofit2.adapter.rxjava.CallExecuteOnSubscribe.call(CallExecuteOnSubscribe.java:24) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.call(OperatorSubscribeOn.java:100) at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)


Solution

  • As I mentioned that if we want to create servicePrincipal then the resource should be http://graph.windows.net or https://graph.microsoft.com.

    So we need to add the permisson to operate the Azure AD Graph API or Microsoft Graph API.

    And don't forget Grant permission.

    I test it with Azure Active Directory API. I works correctly on my side.

    Demo Code:

    ApplicationTokenCredentials credentials = new ApplicationTokenCredentials(client,
                   tenant,
                    key,
                    AzureEnvironment.AZURE);
    
    Azure.Authenticated azureAuthClient = Azure.configure().authenticate(credentials);
    String clusterId = "xxxxxxx";
    ServicePrincipal servicePrincipal =
                    azureAuthClient.servicePrincipals()
                            .define(clusterId)
                            .withNewApplication("http://easycreate.azure.com/" + clusterId)
                            .definePasswordCredential("sppass")
                            .withPasswordValue("StrongPass!12")
                            .attach()
                            .create();
    

    enter image description here