Closed janwei25 closed 3 years ago
@janwei25 Would you let us know your use case, e.g. other Java mgmt libs you already using?
@xccc-msft I didn't find a Lite package for azure frontdoor. We might need to release one if service team is OK with it.
Thanks for the feedback! We are routing this to the appropriate team for follow-up. cc @cdnfdsuppgithub.
@weidongxu-microsoft I'm using a lot of Java mgmt libs for Azure. Among some of them are Network, Compute, KeyVault, Storage, Sql, etc... and many more to make queries for resources. At first, I thought Frontdoor might be within Network but it's not. It would be great if FrontDoor would be added so that we would not need to use the Rest API!
@janwei25 Thanks. We'd like to know a bit whether you are using this lib (https://mvnrepository.com/artifact/com.microsoft.azure/azure-mgmt-network) or this (https://mvnrepository.com/artifact/com.microsoft.azure.network.v2020_06_01/azure-mgmt-network). Note the namespace difference.
The frontdoor java lib would likely be similar to latter.
@weidongxu-microsoft Oh, I'm using the stable SDK version (the former). I looked up the latter version in the github repo and could not find any frontdoor files there so I think frontdoor is not there either. Thanks for the heads up though. I'll be monitoring that repo for latest updates.
@janwei25 thanks for reporting the issue. If you don't mind, could you share a bit about your use cases? e.g.
Thanks much
@nickzhums Hi!
What are you using the management libraries for?
To query and read resource data across a variety of Azure resources across multiple services (Network, Compute, KeyVault, etc...). Long story short, our clients grant us Azure service principals in order to query their resources and we use the Java SDK to get those data. We use the Java SDK because our backend is in Java and we prefer it over directly using the REST API as the Java SDK already handle stuff like client retries and it's very robust!
Anything we can do to improve?
Generally, the SDK is very good however I have some issues with it.
This is not so much an issue with the Java SDK but the underlying REST API. When an API request is made to Azure Mgmt to read a resource and the service principal sent via that API request does not have permissions to read that resource, the API returns a 200 OK with an empty list rather than something like 403 Forbidden. For instance, if client A has a Virtual Machine under resource group A / subscription A and if client A grants us a service principal to query VMs but doesnt grant us permissions to do so, the API would return 200 OK
with []
. This makes it hard for us to distinguish between Insufficient permissions and No Resource and distinguishing them is important for our use case. In order to distinguish them, we had to retrieve their permissions via the Authorization API and check the permissions one by one and also the fact that Azure allows wildcards (*) as permissions and is also unclear on whether permissions are case-sensitive or not make this whole process more complicated than it needs to be. If Azure had returned a 403 here, everything would be easier.
The SDK doesnt have a consistent method of handling errors. For instance, authentication errors may present themselves in various ways. They may actually be returned as a CloudException
with a 401
or they may be returned as an authentication exception, which is embedded inside many layers of wrapped exceptions. I also noticed some instances in which the AppService sends a 401 code back in the message but the error message is something like Internal Server Error...
In short, it is just complicated trying to handle all these edge cases. If there is a consistent way of detecting a certain error, that would be much better!
The SDK does not have predictable naming of resource's fields. For instance, some fields are available in the .inner
object but some are not and it's not clear why. This makes it harder for users to write automated scripts in order to destructure and parse large resource objects. The AWS Java SDK on the other hand handles this very well. Their resource props are always predictable and their methods to get those props are always of the template get{ResourceField}()
so it's easy to write scripts to automate parsing.
The stable Java SDK versions do not cover all bases. For instance, for Private DNS, MySQL and PostgreSQL, I had to use some beta packages instead for those. It would be great if those services can be included in the stable library.
The latest stable version of KeyVault is missing some significant number of fields for the Vault resource. Sql servers (stable version) are also missing like...almost half of the fields.
Documentation on the Java SDK can be improved and be more detailed. On a first glance at https://docs.microsoft.com/en-us/java/api/overview/azure/?view=azure-java-stable, it is not easy to find methods such as listByResourceGroup
which I often use. I had to go manually to this github repo and search for those methods.
@janwei25 Thanks for the info. In short-term, the frontdoor lib will be like what you get as private dns / mysqldb / postgresqldb (i.e., a separate package which likely also start with beta).
@cdnfdsuppgithub Hi Max, is frontdoor service ready for Java mgmt SDK?
@janwei25 Thanks so much for providing the feedback. A lot of useful stuff - we will evaluate those feedback and see how we can improve those aspects. As a side-note, we just released our next generation Java SDK, which addresses some of the things you mentioned above (eg. error handling). The libraries can be found at here. Hope you will be able to use it in some of your future projects :)
Next generation will have private dns in stable.
Error handling will get a ManagementException
for all management operations (exclude a few data-plane operation like key/secret in vault, user/group in RBAC). Note that if resource is not found, it will have exception as well.
Fields naming is a bit hard story. We can change them to e.g. get{field}
but then it will break almost all existing code.
@nickzhums NP. Thx for the link!
@weidongxu-microsoft Thx for the heads up on the error handling improvements in next-gen. I may upgrade it if we have more consistent error handling methods there :D
Thx for the fast response! Looking forward to it.
@janwei25 I talked with docs team, you can search for an API method like this:
https://docs.microsoft.com/en-us/java/api/?term=listByResourceGroup
@anmeng10101 can provide more info
The SDK does not have predictable naming of resource's fields. For instance, some fields are available in the .inner object but some are not and it's not clear why. This makes it harder for users to write automated scripts in order to destructure and parse large resource objects. The AWS Java SDK on the other hand handles this very well. Their resource props are always predictable and their methods to get those props are always of the template get{ResourceField}() so it's easy to write scripts to automate parsing.
If you only need all the properties, the ones in inner
object will be enough (do it recursively since many will have nested objects).
Part of the difference is due to certain abstraction. E.g. StorageAccount
got accountStatuses()
method, which abstracts statusOfPrimary()
and statusOfSecondary()
from inner
.
Some of the abstract tries to improve the usability (e.g. certain inner
will have a String with coma separated values, abstraction will convert it to List).
Also there is some operation method like getKeys()
in StorageAccount
.
@janwei25 , thanks a lot for the feedback about the documents.
Would like to know your normal preference to search a method please? such as find listByResourceGroup
from https://docs.microsoft.com/en-us/java/api/overview/azure/?view=azure-java-stable.
Is it search from 2 highlight search boxes?
@weidongxu-microsoft Yes, I noticed that .inner
usually has all the fields but while it works like...99% of the time, I think there is one instance where because I used the inner object, I got loss of field data instead. It has been a while since I have faced that issue (around 2 months ago) so I forgot all the details but IIRC, it was LocalNetworkGateway
from Network
service. The info I got from LocalNetworkGatewayInner
had a few missing fields in some nested objects vs what I would have gotten if I had used LocalNetworkGateway
instead. I forgot exactly which nested fields they were but they were not critical (prob around 4 to 5 fields). The fields were all complete and available but the SDK did not populate them. It is entirely possible that I made a mistake there though. Not sure, haha.
@anmeng10101 No, I did not use the search bar at all. Maybe Im stupid haha but I was developing for Azure via the Python SDK prior to Java and was used to the format of the documentation there. It was from the docs there that I found out that I could list by resource group.
For instance, if I wanted to search the methods available to retrieve Application Gateways, I just head here for Python https://docs.microsoft.com/en-us/python/api/azure-mgmt-network/azure.mgmt.network.v2020_06_01.operations.applicationgatewaysoperations?view=azure-python and the operations possible are listed very clearly. From this page, I know that I can call list
to list by resource group. It is very intuitive and I could reach that page just via the left menu hierarchy.
So naturally, when I went on to search the docs for the stable Java SDK instead, I used the same method because I assumed it would be consistent. However, my usual navigation methods led me to some page like this https://docs.microsoft.com/en-us/java/api/com.microsoft.azure.management.network?view=azure-java-stable, which is just a large collection of class models and I'm not sure how to find list operations via navigating there at all. I searched through the whole docs and I couldn't find any listByResourceGroup
type operation so I almost gave up but luckily I downloaded this github repo and did a deep term search and found the methods in the /implementation
folder.
Personally, I feel that docs structure should be the same across different language SDKs so that it is predictable. I feel that the way the docs on the Python SDK is structured is much better as I can see straightaway what operations are possible for a resource class. Even with the search bar there, it is hard to search for the specific listByResourceGroup
method I want. Let's say I am a newbie and I have never integrated Azure before. Browsing through the Java docs, I would have never thought that there is even a listByResourceGroup
method available. It is only through the Python SDK that I know that such method should be available.
@janwei25
I see. Yes, it is indeed possible. Again caused by certain abstraction to improve usability (it is not common). Some object would try to take a few closely related inner objects from REST, hence this would happen (since the inner
only refer to one of them).
@weidongxu-microsoft Hi, this is a separate subject. But is there any way of getting the object ID of a service principal via the Java SDK given an ApplicationTokenCredentials
object (clientId, tenantId and clientSecret) being supplied. According to https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals#application-and-service-principal-relationship, the relation between app to service principals are 1:many in a multi-tenant app but since the tenantId
is supplied here, the mapping between clientId
=> objectId
should be 1:1. But I can't seem to find any method in the management API which does this, not even REST. I know for sure though getting the OID is possible because when I call to get an access token via logging in with the service principal credentials and after the parsing the JWT token, I do get an oid
field in the object. However, I can't really get the object ID via any SDK or REST methods at all.
Something like https://docs.microsoft.com/en-us/azure/healthcare-apis/find-identity-object-ids#find-service-principal-object-id for the Java SDK or any other pointers would be great
@janwei25
You can do this
String objectId = azure.accessManagement().servicePrincipals().getByName(credentials.clientId()).id();
Note that listing (with filter) the service principals will require certain AAD permission.
PS: for frontdoor Java SDK, still taking with service on which api-version to release.
@ChenTanyi Please update when package is released.
Sample code (might not be exactly same)
String backendAddress;
String frontendName = "frontend1";
String loadBalancingName = "loadbalancing1";
String healthProbeName = "healthprobe1";
String routingRuleName = "rule1";
String backendPoolName = "backend1";
String frontendEndpointsId = getResourceId("frontendEndpoints", frontendName);
String loadBalancingSettingsId = getResourceId("loadBalancingSettings", loadBalancingName);
String healthProbeSettingsId = getResourceId("healthProbeSettings", healthProbeName);
String backendPoolsId = getResourceId("backendPools", backendPoolName);
FrontDoor frontDoor = frontDoorManager.frontDoors().define(fdName)
.withRegion("global")
.withExistingResourceGroup(rgName)
.withFrontendEndpoints(Collections.singletonList(
new FrontendEndpointInner()
.withName(frontendName)
.withHostname(fdName + ".azurefd.net")
.withSessionAffinityEnabledState(SessionAffinityEnabledState.DISABLED)
))
.withBackendPools(Collections.singletonList(
new BackendPool().withName(backendPoolName).withBackends(Collections.singletonList(
new Backend()
.withAddress(backendAddress)
.withEnabledState(BackendEnabledState.ENABLED)
.withBackendHostHeader(backendAddress)
.withHttpPort(80)
.withHttpsPort(443)
.withPriority(1)
.withWeight(50)
))
.withLoadBalancingSettings(new SubResource().withId(loadBalancingSettingsId))
.withHealthProbeSettings(new SubResource().withId(healthProbeSettingsId))
))
.withLoadBalancingSettings(Collections.singletonList(
new LoadBalancingSettingsModel()
.withName(loadBalancingName)
.withSampleSize(4)
.withSuccessfulSamplesRequired(2)
.withAdditionalLatencyMilliseconds(0)
))
.withHealthProbeSettings(Collections.singletonList(
new HealthProbeSettingsModel()
.withName(healthProbeName)
.withEnabledState(HealthProbeEnabled.ENABLED)
.withPath("/")
.withProtocol(FrontDoorProtocol.HTTPS)
.withHealthProbeMethod(FrontDoorHealthProbeMethod.HEAD)
.withIntervalInSeconds(30)
))
.withRoutingRules(Collections.singletonList(
new RoutingRule()
.withName(routingRuleName)
.withEnabledState(RoutingRuleEnabledState.ENABLED)
.withFrontendEndpoints(Collections.singletonList(new SubResource().withId(frontendEndpointsId)))
.withAcceptedProtocols(Arrays.asList(FrontDoorProtocol.HTTP, FrontDoorProtocol.HTTPS))
.withPatternsToMatch(Collections.singletonList("/*"))
.withRouteConfiguration(new ForwardingConfiguration()
.withForwardingProtocol(FrontDoorForwardingProtocol.HTTPS_ONLY)
.withBackendPool(new SubResource().withId(backendPoolsId)))
))
.create();
private String getResourceId(String type, String name) {
return String.format("/subscriptions/%1$s/resourceGroups/%2$s/providers/Microsoft.Network/frontdoors/%3$s/%4$s/%5$s",
subscriptionId, rgName, fdName, type, name);
}
Thanks guys for the fast response. I tested and it worked with my use case! :+1:
If i'm not mistaken, there is no frontdoor package available for java. https://azure.github.io/azure-sdk/releases/latest/all/#java states Python, .NET and JavaScript have frontdoor packages but Java doesn't have them. Please add them ASAP. Thanks!