Introducing the new Azure Resource Management Libraries for Java

Weidong Xu

The latest Azure Resource Management Libraries for Java is a result of our efforts to create a resource management client library that is user-friendly and idomatic to the Java ecosystem. These new libraries provide a higher-level, object-oriented API for managing Azure resources, that is optimized for ease of use, succinctness, and consistency.

In this post, we’ll present an overview of those libraries as well as several highlighted features.

Features

Fluent Interfaces and Optimized Workflow

In order to accomodate to the Java development ecosystem as well as minimize the effort for future migration, Fluent patterns / interfaces are still used in the new resource management libraries.

The Fluent API is also designed to optimize your workflow when you are creating a resource that has other dependencies. For example, a Virtual Machine creation requires a Resource Group as well as network related configurations. Typically you would have to create those resources individually. However, our Java resource management libraries does the heavylifting with certain underlying logic.

For example, when you create a Virtual Machine instance, the Fluent API will prompt you to provide information for dependency resources and create those resources underneath, as part of the Virtual Machine creation flow.

VirtualMachine linuxVM = azureResourceManager.virtualMachines().define("myLinuxVM")
    .withRegion(Region.US_EAST)
    .withNewResourceGroup(rgName)
    .withNewPrimaryNetwork("10.0.0.0/28")
    .withPrimaryPrivateIPAddressDynamic()
    .withNewPrimaryPublicIPAddress("mylinuxvm")
    .withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_16_04_LTS)
    .withRootUsername(username)
    .withSsh(sshKey)
    .withSize(VirtualMachineSizeTypes.STANDARD_D2_v2)
    .create();

The following code snippet shows another example where you can create a Function App with required Storage Account and Service Plan

Creatable<StorageAccount> creatableStorageAccount = azureResourceManager.storageAccounts()
    .define(storageAccountName)
    .withRegion(Region.US_EAST)
    .withExistingResourceGroup(rgName)
    .withGeneralPurposeAccountKindV2()
    .withSku(StorageAccountSkuType.STANDARD_LRS);

Creatable<AppServicePlan> creatableAppServicePlan = azureResourceManager.appServicePlans()
    .define(appServicePlanName)
    .withRegion(Region.US_EAST)
    .withExistingResourceGroup(rgName)
    .withPricingTier(PricingTier.STANDARD_S1)
    .withOperatingSystem(OperatingSystem.LINUX);

FunctionApp linuxFunctionApp = azureResourceManager.functionApps().define(functionAppName)
    .withRegion(Region.US_EAST)
    .withExistingResourceGroup(rgName)
    .withNewLinuxAppServicePlan(creatableAppServicePlan)
    .withBuiltInImage(FunctionRuntimeStack.JAVA_8)
    .withNewStorageAccount(creatableStorageAccount)
    .withHttpsOnly(true)
    .withAppSetting("WEBSITE_RUN_FROM_PACKAGE", functionAppPackageUrl)
    .create();

You can also batch create and delete Managed Disk Instances.

List<String> diskNames = Arrays.asList("datadisk1", "datadisk2");

List<Creatable<Disk>> creatableDisks = diskNames.stream()
    .map(diskName -> azureResourceManager.disks()
        .define(diskName)
        .withRegion(Region.US_EAST)
        .withExistingResourceGroup(rgName)
        .withData()
        .withSizeInGB(10)
        .withSku(DiskSkuTypes.STANDARD_LRS))
    .collect(Collectors.toList());

Collection<Disk> disks = azureResourceManager.disks().create(creatableDisks).values();

azureResourceManager.disks().deleteByIds(disks.stream().map(Disk::id).collect(Collectors.toList()));

Authorization via MSAL / Azure Identity

MSAL integrates with the Microsoft identity platform (v2.0) endpoint.

In new management libraries, MSAL is supported via the new Azure Identity library.

For most scenarios, DefaultAzureCredential is the recommended approach as it combines credentials commonly used to authenticate when deployed, with credentials used to authenticate in a development environment. It will attempt to authenticate via the following mechanisms in order

DefaultAzureCredential authentication flow

  • Environment – The DefaultAzureCredential will read account information specified via environment variables and use it to authenticate.
  • Managed Identity – If the application is deployed to an Azure host with Managed Identity enabled, the DefaultAzureCredential will authenticate with that account.
  • IntelliJ – If the developer has authenticated via Azure Toolkit for IntelliJ, the DefaultAzureCredential will authenticate with that account.
  • Visual Studio Code – If the developer has authenticated via the Visual Studio Code Azure Account plugin, the DefaultAzureCredential will authenticate with that account.
  • Azure CLI – If the developer has authenticated an account via the Azure CLI az login command, the DefaultAzureCredential will authenticate with that account.

The following code snippet demonstrates how to authenticate to Azure using the DefaultAzureCredential

AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE);    // Assume Global Cloud is used
AzureResourceManager azureResourceManager = AzureResourceManager
    .authenticate(new DefaultAzureCredentialBuilder().build(), profile)
    .withDefaultSubscription();

See Azure Identity Credential Types for all of the available developer credentials.

As noted above, DefaultAzureCredential will attempt to authenticate with Managed Identity via the ManagedIdentityCredential, but you can also explicitly use it like this:

AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE);    // Assume Global Cloud is used
TokenCredential credential = new ManagedIdentityCredentialBuilder()
    .build();
AzureResourceManager azureResourceManager = AzureResourceManager
    .authenticate(credential, profile)
    .withDefaultSubscription();

To provide further flexibility, you can also use ChainedTokenCredential to construct a customized chain of credentails for your authentication flow. The following code snippet demonstrates a scenario where the user needs to add AzureCliCredential and ManagedIdentityCredential to the credential chain:

AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE);
TokenCredential tokenCredential = new ChainedTokenCredentialBuilder()
    .addLast(new ManagedIdentityCredentialBuilder().build())
    .addLast(new AzureCliCredentialBuilder().build())
    .build();
AzureResourceManager azureResourceManager = AzureResourceManager
    .authenticate(tokenCredential, profile)
    .withDefaultSubscription();

Please note that for the code snippets above, the management libraries require a subscription ID and a tenant ID, which can be configured via environment variable as AZURE_SUBSCRIPTION_ID and AZURE_TENANT_ID, or via an alternative constructor of AzureProfile. The tenant ID can be left unspecified or as null, if you do not use the management libraries to manage Active Directory or Role-Based Access Control.

HTTP Client as Plug-in

Previously, OkHttp client was hard-wired as the only HTTP client of management libraries. This causes some difficulty for the user who wishes to use a different HTTP client.

With the new management libraries, you are now free to choose a different HttpClient implementation, or even provide a custom one.

By default, management libraries use the HTTP client loaded in runtime. Specifically, you can configure and use Netty HTTP client.

HttpClient httpClient = new NettyAsyncHttpClientBuilder()
    .readTimeout(Duration.ofMinutes(2))
    .build();

AzureResourceManager azureResourceManager = AzureResourceManager
    .configure()
    .withHttpClient(httpClient)
    ...

Better Control Over Long-Running Operations (LRO)

Long-running operations is complicated and previously completely abstracted-away by Fluent interface.

The new management libraries offer better control over the provisioning of some frequently used Azure resources.

Let’s assume you have an ARM template to be deployed to a resource group, and you know it usually takes a bit more than 2 minutes. It is more economical to begin deployment, then delay 2 minutes before polling the result.

final Duration delayedInterval = Duration.ofMinutes(2);
final Duration pollInterval = Duration.ofSeconds(10);

// begin deployment
Accepted<Deployment> acceptedDeployment = azureResourceManager.deployments()
    .define(deploymentName)
    .withExistingResourceGroup(rgName)
    .withTemplateLink(templateUri, contentVersion)
    .withParametersLink(parametersUri, contentVersion)
    .withMode(DeploymentMode.COMPLETE)
    .beginCreate();
Deployment provisioningDeployment = acceptedDeployment.getActivationResponse().getValue();

LongRunningOperationStatus pollStatus = acceptedDeployment.getActivationResponse().getStatus();
long delayInMills = delayedInterval.toMillis();
while (!pollStatus.isComplete()) {
    Thread.sleep(delayInMills);

    // poll
    PollResponse<?> pollResponse = acceptedDeployment.getSyncPoller().poll();
    pollStatus = pollResponse.getStatus();
    delayInMills = pollInterval.toMillis();
}
// pollStatus == LongRunningOperationStatus.SUCCESSFULLY_COMPLETED, if successful

// final result
Deployment deployment = acceptedDeployment.getFinalResult();

And it is also common use case, to begin deleting a resource group, then check it back later.

Accepted<Void> acceptedDelete = azureResourceManager.resourceGroups().beginDeleteByName(rgName);

// do other stuffs

acceptedDelete.getFinalResult();

Consistent Exception Handling for Server Errors

If the server reports an error, the new management libraries throw ManagementException.

ManagementException.getValue().getCode() and ManagementException.getValue().getMessage() provides an error code and error message from the server.

ManagementException.getResponse() provides the raw HTTP response.

Technical notes: The error on connection itself is not a ManagementException. Its type depends on the HttpClient implementation used.

Service Coverage

We support some new Java-based Azure services such as the Spring Cloud service.

SpringService service = azureResourceManager.springServices().define(serviceName)
    .withRegion(Region.US_EAST)
    .withExistingResourceGroup(rgName)
    .create();

Here’s the complete list of services that are currently supported by the new resource management libraries:

  • App Service
  • Authorization
  • CDN
  • Compute
  • Container Instance
  • Container Registry
  • Container Service
  • Cosmos DB
  • DNS
  • Event Hubs
  • Key Vault
  • Managed Identity
  • Monitor (Insight)
  • Network
  • Private DNS
  • Redis
  • Resources
  • Spring Cloud
  • SQL
  • Storage
  • Traffic Manager

Note that some of the packages are in a preview state. For production uses, please prioritize using the stable packages in your projects.

You can find the full list of libraries with links to packages, code, and docs on the Azure SDK Releases page

Documentation

To learn more details about the new Java resource management libraries, please see the documentation here

Azure SDK Blog Contributions

Thank you for reading this Azure SDK blog post! We hope that you learned something new and welcome you to share this post. We are open to Azure SDK blog contributions. Please contact us at azsdkblog@microsoft.com with your topic and we’ll get you setup as a guest blogger.

0 comments

Discussion is closed.

Feedback usabilla icon