org.jacoco
jacoco-maven-plugin
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/README.md b/sdk/resourcemanager/azure-resourcemanager/src/samples/README.md
new file mode 100644
index 000000000000..3978a197a115
--- /dev/null
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/README.md
@@ -0,0 +1,94 @@
+---
+page_type: sample
+languages:
+ - java
+products:
+ - azure
+urlFragment: azure-resourcemanager-samples
+---
+
+# Azure Resource Manager samples for Java
+
+This contains Java samples for managing Azure resources with the Azure Resource Manager client library. Each sample is
+self-contained, focuses on a specific scenario, and exposes a `main()` entrypoint so it can be run directly.
+
+## Getting started
+
+Getting started is explained in detail [here][RESOURCEMANAGER_README_GETTING_STARTED].
+Please refer to it to add the dependency and configure the authentication environment variables.
+
+For general documentation on how to use the management libraries, please [visit here][MGMT_SDK_LINK].
+For additional code snippets grouped by service, see [SAMPLE.md][RESOURCEMANAGER_SAMPLE_DOC].
+
+## Sample details
+
+### App Service
+
+- [Connect a web app to a SQL database (passwordless)][sample_connect_web_app_to_sql_database]
+- [Connect a web app to a storage account (passwordless)][sample_connect_web_app_to_storage_account]
+- [Deploy an image from Azure Container Registry to a Linux web app][sample_deploy_image_from_acr_to_linux_web_app]
+- [Manage web app deployment slots][sample_manage_web_app_slots]
+- [Manage a web app with a custom domain][sample_manage_web_app_with_custom_domain]
+- [Scale a web app across multiple regions with a Traffic Manager][sample_scale_web_app_with_traffic_manager]
+
+### Authorization (Microsoft Entra ID / RBAC)
+
+- [Manage users, groups and roles][sample_manage_users_groups_and_roles]
+- [Manage service principals][sample_manage_service_principal]
+
+### Compute
+
+- [Create a virtual machine from a custom image][sample_create_vm_using_custom_image_from_vm]
+- [Create a virtual machine from a specialized managed disk (snapshot)][sample_create_vm_using_specialized_disk_from_snapshot]
+- [Create multiple virtual machines in parallel in the same network][sample_manage_vms_in_parallel]
+
+### Container Registry
+
+- [Manage container registries][sample_manage_container_registry]
+
+### Container Service
+
+- [Manage an Azure Kubernetes Service (AKS) cluster][sample_manage_kubernetes_cluster]
+
+### SQL Database
+
+- [Create and manage a SQL database][sample_manage_sql_database]
+- [Manage SQL databases across multiple regions][sample_manage_sql_databases_across_regions]
+
+## Next steps
+
+Start using the Azure Resource Manager Java SDK in your solutions. Our SDK details can be found at [SDK README][RESOURCEMANAGER_SDK_README].
+
+For more information about other Azure Management SDKs, refer [here][MGMT_SDK_LINK].
+
+## Contributing
+
+For details on contributing to this repository, see the [contributing guide][RESOURCEMANAGER_README_CONTRIBUTING].
+
+1. Fork it
+1. Create your feature branch (`git checkout -b my-new-feature`)
+1. Commit your changes (`git commit -am 'Add some feature'`)
+1. Push to the branch (`git push origin my-new-feature`)
+1. Create new Pull Request
+
+
+[MGMT_SDK_LINK]: https://aka.ms/azsdk/java/mgmt
+[RESOURCEMANAGER_SDK_README]: https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/resourcemanager/azure-resourcemanager
+[RESOURCEMANAGER_README_GETTING_STARTED]: https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/resourcemanager/azure-resourcemanager#getting-started
+[RESOURCEMANAGER_README_CONTRIBUTING]: https://github.com/Azure/azure-sdk-for-java/blob/main/CONTRIBUTING.md
+[RESOURCEMANAGER_SAMPLE_DOC]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/docs/SAMPLE.md
+[sample_connect_web_app_to_sql_database]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToSqlDatabase.java
+[sample_connect_web_app_to_storage_account]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToStorageAccount.java
+[sample_deploy_image_from_acr_to_linux_web_app]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/DeployImageFromAcrToLinuxWebApp.java
+[sample_manage_web_app_slots]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppSlots.java
+[sample_manage_web_app_with_custom_domain]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppWithCustomDomain.java
+[sample_scale_web_app_with_traffic_manager]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ScaleWebAppWithTrafficManager.java
+[sample_manage_users_groups_and_roles]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/ManageUsersGroupsAndRoles.java
+[sample_manage_service_principal]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/ManageServicePrincipal.java
+[sample_create_vm_using_custom_image_from_vm]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/compute/samples/CreateVirtualMachineUsingCustomImageFromVM.java
+[sample_create_vm_using_specialized_disk_from_snapshot]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/compute/samples/CreateVirtualMachineUsingSpecializedDiskFromSnapshot.java
+[sample_manage_vms_in_parallel]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/compute/samples/ManageVirtualMachinesInParallel.java
+[sample_manage_container_registry]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/containerregistry/samples/ManageContainerRegistry.java
+[sample_manage_kubernetes_cluster]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/containerservice/samples/ManageKubernetesCluster.java
+[sample_manage_sql_database]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/sql/samples/ManageSqlDatabase.java
+[sample_manage_sql_databases_across_regions]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/sql/samples/ManageSqlDatabasesAcrossRegions.java
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToSqlDatabase.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToSqlDatabase.java
index ac9e33f2797c..97d48ab6fdf7 100644
--- a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToSqlDatabase.java
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToSqlDatabase.java
@@ -45,6 +45,7 @@ public static boolean runSample(AzureResourceManager azureResourceManager) {
try {
// Create a web app with a system-assigned managed identity; that identity is used to reach SQL.
+ // HTTPS-only is enforced; minimum TLS 1.2 and FTPS-only are already the App Service defaults.
WebApp app = azureResourceManager.webApps()
.define(appName)
.withRegion(Region.US_WEST)
@@ -52,6 +53,7 @@ public static boolean runSample(AzureResourceManager azureResourceManager) {
.withNewWindowsPlan(PricingTier.STANDARD_S1)
.withJavaVersion(JavaVersion.JAVA_11)
.withWebContainer(WebContainer.TOMCAT_9_0_NEWEST)
+ .withHttpsOnly(true)
.withSystemAssignedManagedServiceIdentity()
.create();
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToStorageAccount.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToStorageAccount.java
index 0e453696b623..f3c727ef94ba 100644
--- a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToStorageAccount.java
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ConnectWebAppToStorageAccount.java
@@ -4,25 +4,41 @@
package com.azure.resourcemanager.appservice.samples;
import com.azure.core.credential.TokenCredential;
+import com.azure.core.exception.HttpResponseException;
import com.azure.core.http.policy.HttpLogDetailLevel;
import com.azure.core.management.Region;
import com.azure.core.management.profile.AzureProfile;
import com.azure.core.models.AzureCloud;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.resourcemanager.AzureResourceManager;
-import com.azure.resourcemanager.appservice.models.ConnectionStringType;
-import com.azure.resourcemanager.appservice.models.JavaVersion;
import com.azure.resourcemanager.appservice.models.PricingTier;
+import com.azure.resourcemanager.appservice.models.RuntimeStack;
import com.azure.resourcemanager.appservice.models.WebApp;
-import com.azure.resourcemanager.appservice.models.WebContainer;
+import com.azure.resourcemanager.authorization.models.BuiltInRole;
+import com.azure.resourcemanager.resources.fluentcore.utils.ResourceManagerUtils;
import com.azure.resourcemanager.samples.SampleUtils;
import com.azure.resourcemanager.storage.models.StorageAccount;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.time.Duration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
/**
- * Azure App Service sample for connecting a web app to a storage account.
- * - Create a storage account
- * - Create a web app
- * - Add the storage account connection string to the web app settings
+ * Azure App Service sample for connecting a web app to a storage account without secrets (passwordless).
+ * - Create a storage account with shared-key access disabled
+ * - Create a Node.js web app with a system-assigned managed identity
+ * - Grant that identity the Storage Blob Data Contributor role on the account
+ * - Expose only the blob endpoint (no account key or connection string) to the web app
+ * - Deploy a small Node.js app that reads/writes a blob using that managed identity
+ *
+ * The deployed app authenticates to Blob storage with {@code DefaultAzureCredential}, which picks up the web app's
+ * managed identity. Its source is under {@code src/samples/resources/appservice/mi-blob-verify}.
*/
public final class ConnectWebAppToStorageAccount {
@@ -39,30 +55,57 @@ public static boolean runSample(AzureResourceManager azureResourceManager) {
final String containerName = SampleUtils.randomResourceName(azureResourceManager, "jcontainer", 20);
try {
- // Create a storage account for the web app to use.
+ // Create a storage account with shared-key access disabled, so data-plane access requires
+ // Microsoft Entra credentials (passwordless) rather than an account key.
StorageAccount storageAccount = azureResourceManager.storageAccounts()
.define(storageName)
.withRegion(Region.JAPAN_EAST)
.withNewResourceGroup(rgName)
+ .disableSharedKeyAccess()
.create();
- String accountKey = storageAccount.getKeys().get(0).value();
- String connectionString = String.format("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s",
- storageAccount.name(), accountKey);
-
- // Create a web app and store the storage connection string in its settings.
+ // Create a Node.js web app with a system-assigned managed identity and expose only the blob endpoint.
+ // HTTPS-only is enforced here; minimum TLS 1.2 and FTPS-only are already the App Service defaults.
WebApp app = azureResourceManager.webApps()
.define(appName)
.withRegion(Region.JAPAN_EAST)
.withExistingResourceGroup(rgName)
- .withNewWindowsPlan(PricingTier.STANDARD_S1)
- .withJavaVersion(JavaVersion.JAVA_11)
- .withWebContainer(WebContainer.TOMCAT_9_0_NEWEST)
- .withConnectionString("storage.connectionString", connectionString, ConnectionStringType.CUSTOM)
- .withAppSetting("storage.containerName", containerName)
+ .withNewLinuxPlan(PricingTier.STANDARD_S1)
+ .withBuiltInImage(RuntimeStack.NODEJS_22_LTS)
+ .withHttpsOnly(true)
+ .withSystemAssignedManagedServiceIdentity()
+ .withAppSetting("STORAGE_BLOB_ENDPOINT", storageAccount.endPoints().primary().blob())
+ .withAppSetting("STORAGE_CONTAINER_NAME", containerName)
+ .create();
+
+ // Grant the web app's managed identity data-plane access to blobs (least-privilege, passwordless).
+ azureResourceManager.accessManagement()
+ .roleAssignments()
+ .define(SampleUtils.randomUuid(azureResourceManager))
+ .forObjectId(app.systemAssignedManagedServiceIdentityPrincipalId())
+ .withBuiltInRole(BuiltInRole.STORAGE_BLOB_DATA_CONTRIBUTOR)
+ .withResourceScope(storageAccount)
.create();
+ // Deploy the small Node.js app that touches a blob using the web app's managed identity.
+ // A freshly created web app may briefly have an initial deployment in progress, so retry on HTTP 409.
+ File appPackage = nodeAppPackage();
+ for (int i = 0;; i++) {
+ try {
+ app.zipDeploy(appPackage);
+ break;
+ } catch (HttpResponseException e) {
+ if (i >= 5 || e.getResponse() == null || e.getResponse().getStatusCode() != 409) {
+ throw e;
+ }
+ ResourceManagerUtils.sleep(Duration.ofSeconds(30));
+ }
+ }
+
System.out.println("Connected web app " + app.name() + " to storage account " + storageAccount.name());
+ // To verify manually, browse to https://.azurewebsites.net/ once role assignment has propagated;
+ // the app returns {"ok":true,...} when it can read/write a blob using its managed identity.
+ System.out.println("Verify passwordless access at: https://" + app.defaultHostname() + "/");
return true;
} finally {
azureResourceManager.resourceGroups().beginDeleteByName(rgName);
@@ -92,6 +135,41 @@ public static void main(String[] args) {
}
}
+ /**
+ * Builds a zip package (server.js + package.json) for the small Node.js verification app from the sample
+ * resources. The verifier has no npm dependencies, so App Service deploys it without a build step.
+ *
+ * @return the zip package file
+ */
+ private static File nodeAppPackage() {
+ String[] files = { "server.js", "package.json" };
+ try {
+ File zipFile = File.createTempFile("mi-blob-verify", ".zip");
+ zipFile.deleteOnExit();
+ try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
+ byte[] buffer = new byte[4096];
+ for (String file : files) {
+ zos.putNextEntry(new ZipEntry(file));
+ try (InputStream is = ConnectWebAppToStorageAccount.class
+ .getResourceAsStream("/appservice/mi-blob-verify/" + file)) {
+ if (is == null) {
+ throw new FileNotFoundException(
+ "Sample resource not found on classpath: /appservice/mi-blob-verify/" + file);
+ }
+ int read;
+ while ((read = is.read(buffer)) > 0) {
+ zos.write(buffer, 0, read);
+ }
+ }
+ zos.closeEntry();
+ }
+ }
+ return zipFile;
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
private ConnectWebAppToStorageAccount() {
}
}
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/DeployImageFromAcrToLinuxWebApp.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/DeployImageFromAcrToLinuxWebApp.java
index 2018a559c3d1..f1fce8e70535 100644
--- a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/DeployImageFromAcrToLinuxWebApp.java
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/DeployImageFromAcrToLinuxWebApp.java
@@ -52,6 +52,7 @@ public static boolean runSample(AzureResourceManager azureResourceManager) {
String privateImage = azureRegistry.loginServerUrl() + "/samples/tomcat:latest";
// Create a Linux web app that pulls its image from the private registry.
+ // HTTPS-only is enforced; minimum TLS 1.2 and FTPS-only are already the App Service defaults.
WebApp app = azureResourceManager.webApps()
.define(appName)
.withRegion(region)
@@ -60,6 +61,7 @@ public static boolean runSample(AzureResourceManager azureResourceManager) {
.withPrivateRegistryImage(privateImage, "https://" + azureRegistry.loginServerUrl())
.withCredentials(acrCredentials.username(), acrCredentials.accessKeys().get(AccessKeyType.PRIMARY))
.withAppSetting("PORT", "8080")
+ .withHttpsOnly(true)
.create();
System.out.println("Deployed image " + privateImage + " to web app " + app.defaultHostname());
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppSlots.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppSlots.java
index 42961bfd4bea..18caac9aaaac 100644
--- a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppSlots.java
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppSlots.java
@@ -39,6 +39,7 @@ public static boolean runSample(AzureResourceManager azureResourceManager) {
try {
// Create a web app running Tomcat on a Windows plan.
+ // HTTPS-only is enforced; minimum TLS 1.2 and FTPS-only are already the App Service defaults.
WebApp app = azureResourceManager.webApps()
.define(appName)
.withRegion(Region.US_EAST)
@@ -46,6 +47,7 @@ public static boolean runSample(AzureResourceManager azureResourceManager) {
.withNewWindowsPlan(PricingTier.STANDARD_S1)
.withJavaVersion(JavaVersion.JAVA_11)
.withWebContainer(WebContainer.TOMCAT_9_0_NEWEST)
+ .withHttpsOnly(true)
.create();
// Create a staging deployment slot that inherits the production configuration.
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppWithCustomDomain.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppWithCustomDomain.java
index d3a67aac24a6..bfe32f0a2650 100644
--- a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppWithCustomDomain.java
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ManageWebAppWithCustomDomain.java
@@ -39,11 +39,13 @@ public static boolean runSample(AzureResourceManager azureResourceManager) {
try {
// Create a web app with a new app service plan.
+ // HTTPS-only is enforced; minimum TLS 1.2 and FTPS-only are already the App Service defaults.
WebApp app = azureResourceManager.webApps()
.define(appName)
.withRegion(Region.US_WEST)
.withNewResourceGroup(rgName)
.withNewWindowsPlan(PricingTier.STANDARD_S1)
+ .withHttpsOnly(true)
.create();
// Purchase an Azure-managed domain.
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ScaleWebAppWithTrafficManager.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ScaleWebAppWithTrafficManager.java
index 743a01e4a1e8..542287237d4e 100644
--- a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ScaleWebAppWithTrafficManager.java
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/appservice/samples/ScaleWebAppWithTrafficManager.java
@@ -97,6 +97,7 @@ private static WebApp createWebApp(AzureResourceManager azureResourceManager, St
.withExistingLinuxPlan(plan)
.withExistingResourceGroup(rgName)
.withBuiltInImage(RuntimeStack.JAVA_17_JAVA17)
+ .withHttpsOnly(true)
.create();
}
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/ManageServicePrincipal.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/ManageServicePrincipal.java
new file mode 100644
index 000000000000..7d233904ef8a
--- /dev/null
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/ManageServicePrincipal.java
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.resourcemanager.authorization.samples;
+
+import com.azure.core.credential.TokenCredential;
+import com.azure.core.http.policy.HttpLogDetailLevel;
+import com.azure.core.management.profile.AzureProfile;
+import com.azure.core.models.AzureCloud;
+import com.azure.identity.ClientSecretCredentialBuilder;
+import com.azure.identity.DefaultAzureCredentialBuilder;
+import com.azure.resourcemanager.AzureResourceManager;
+import com.azure.resourcemanager.authorization.models.ActiveDirectoryApplication;
+import com.azure.resourcemanager.authorization.models.BuiltInRole;
+import com.azure.resourcemanager.authorization.models.PasswordCredential;
+import com.azure.resourcemanager.authorization.models.RoleAssignment;
+import com.azure.resourcemanager.authorization.models.ServicePrincipal;
+import com.azure.resourcemanager.resources.fluentcore.utils.ResourceManagerUtils;
+import com.azure.resourcemanager.samples.SampleUtils;
+
+import java.time.Duration;
+import java.util.function.Consumer;
+
+/**
+ * Azure service principal sample for managing service principals.
+ * - Create an Active Directory application
+ * - Create a service principal for the application and assign it the Reader role on the subscription
+ * - Use the service principal's credential to list the subscription's virtual machines
+ * - Update the application (add a reply URL)
+ * - Update the service principal to revoke its password credential and role
+ * - Delete the application (which also deletes the service principal)
+ */
+public final class ManageServicePrincipal {
+ /**
+ * Main function which runs the actual sample.
+ *
+ * @param azureResourceManager instance of AzureResourceManager
+ * @param profile the profile the sample is running in
+ * @return true if sample runs successfully
+ */
+ public static boolean runSample(AzureResourceManager azureResourceManager, AzureProfile profile) {
+ final String appName = SampleUtils.randomResourceName(azureResourceManager, "adapp", 20);
+ final String passwordName = SampleUtils.randomResourceName(azureResourceManager, "password", 20);
+ final PasswordHolder passwordHolder = new PasswordHolder();
+ ActiveDirectoryApplication application = null;
+ try {
+ // Create an Active Directory application.
+ application = azureResourceManager.accessManagement()
+ .activeDirectoryApplications()
+ .define(appName)
+ .withSignOnUrl("https://github.com/Azure/azure-sdk-for-java/" + appName)
+ .create();
+
+ // Create a service principal for the application, add a password credential, and assign the
+ // Reader role on the subscription (least privilege: the sample only needs to read resources below).
+ // The service principal must share the application's display name.
+ ServicePrincipal servicePrincipal = azureResourceManager.accessManagement()
+ .servicePrincipals()
+ .define(appName)
+ .withExistingApplication(application)
+ .definePasswordCredential(passwordName)
+ .withPasswordConsumer(passwordHolder)
+ .attach()
+ .withNewRoleInSubscription(BuiltInRole.READER, azureResourceManager.subscriptionId())
+ .create();
+
+ // Wait for the service principal and role assignment to propagate.
+ ResourceManagerUtils.sleep(Duration.ofSeconds(15));
+
+ // Use the service principal's credential to access the subscription and list its virtual machines.
+ TokenCredential spCredential
+ = new ClientSecretCredentialBuilder().tenantId(azureResourceManager.tenantId())
+ .clientId(servicePrincipal.applicationId())
+ .clientSecret(passwordHolder.password)
+ .authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
+ .build();
+ long vmCount = AzureResourceManager.authenticate(spCredential, profile)
+ .withDefaultSubscription()
+ .virtualMachines()
+ .list()
+ .stream()
+ .count();
+ System.out.println("Service principal listed " + vmCount + " virtual machine(s) in the subscription.");
+
+ // Update the application: add a reply URL.
+ application.update().withReplyUrl("https://github.com/Azure/azure-sdk-for-java").apply();
+
+ // Update the service principal: revoke its password credential and role assignment.
+ ServicePrincipal.Update spUpdate = servicePrincipal.update().withoutCredential(passwordName);
+ for (RoleAssignment roleAssignment : servicePrincipal.roleAssignments()) {
+ spUpdate.withoutRole(roleAssignment);
+ }
+ spUpdate.apply();
+
+ System.out.println("Revoked the service principal's password credential and role assignment.");
+ return true;
+ } finally {
+ if (application != null) {
+ // Deleting the application also deletes its service principal.
+ azureResourceManager.accessManagement().activeDirectoryApplications().deleteById(application.id());
+ }
+ }
+ }
+
+ private static final class PasswordHolder implements Consumer {
+ private String password;
+
+ @Override
+ public void accept(PasswordCredential passwordCredential) {
+ this.password = passwordCredential.value();
+ }
+ }
+
+ /**
+ * Main entry point.
+ *
+ * @param args the parameters
+ */
+ public static void main(String[] args) {
+ try {
+ final AzureProfile profile = new AzureProfile(AzureCloud.AZURE_PUBLIC_CLOUD);
+ final TokenCredential credential = new DefaultAzureCredentialBuilder()
+ .authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
+ .build();
+
+ AzureResourceManager azureResourceManager = AzureResourceManager.configure()
+ .withLogLevel(HttpLogDetailLevel.BASIC)
+ .authenticate(credential, profile)
+ .withDefaultSubscription();
+
+ runSample(azureResourceManager, profile);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private ManageServicePrincipal() {
+ }
+}
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/ManageUsersGroupsAndRoles.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/ManageUsersGroupsAndRoles.java
new file mode 100644
index 000000000000..ce598448f2f1
--- /dev/null
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/ManageUsersGroupsAndRoles.java
@@ -0,0 +1,167 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.resourcemanager.authorization.samples;
+
+import com.azure.core.credential.TokenCredential;
+import com.azure.core.http.policy.HttpLogDetailLevel;
+import com.azure.core.management.profile.AzureProfile;
+import com.azure.core.models.AzureCloud;
+import com.azure.identity.DefaultAzureCredentialBuilder;
+import com.azure.resourcemanager.AzureResourceManager;
+import com.azure.resourcemanager.authorization.models.ActiveDirectoryApplication;
+import com.azure.resourcemanager.authorization.models.ActiveDirectoryGroup;
+import com.azure.resourcemanager.authorization.models.ActiveDirectoryUser;
+import com.azure.resourcemanager.authorization.models.BuiltInRole;
+import com.azure.resourcemanager.authorization.models.RoleAssignment;
+import com.azure.resourcemanager.authorization.models.RoleDefinition;
+import com.azure.resourcemanager.authorization.models.ServicePrincipal;
+import com.azure.resourcemanager.resources.fluentcore.utils.ResourceManagerUtils;
+import com.azure.resourcemanager.samples.SampleUtils;
+
+import java.time.Duration;
+
+/**
+ * Azure Users, Groups and Roles sample.
+ * - Create a user
+ * - Assign a role to the user, then revoke it
+ * - Get a role definition by scope and role name
+ * - Create a service principal and assign a role to it
+ * - Create two Active Directory groups
+ * - Add the user, the service principal and the first group as members of the second group
+ */
+public final class ManageUsersGroupsAndRoles {
+ /**
+ * Main function which runs the actual sample.
+ *
+ * @param azureResourceManager instance of AzureResourceManager
+ * @return true if sample runs successfully
+ */
+ public static boolean runSample(AzureResourceManager azureResourceManager) {
+ final String subscriptionId = azureResourceManager.subscriptionId();
+ final String userEmail = SampleUtils.randomResourceName(azureResourceManager, "test", 15);
+ final String userName = userEmail.replace("test", "Test ");
+ final String spName = SampleUtils.randomResourceName(azureResourceManager, "sp", 15);
+ final String raName1 = SampleUtils.randomUuid(azureResourceManager);
+ final String raName2 = SampleUtils.randomUuid(azureResourceManager);
+ final String groupEmail1 = SampleUtils.randomResourceName(azureResourceManager, "group1", 15);
+ final String groupEmail2 = SampleUtils.randomResourceName(azureResourceManager, "group2", 15);
+ final String groupName1 = groupEmail1.replace("group1", "Group ");
+ final String groupName2 = groupEmail2.replace("group2", "Group ");
+ ActiveDirectoryUser user = null;
+ ServicePrincipal sp = null;
+ RoleAssignment spRoleAssignment = null;
+ ActiveDirectoryGroup group1 = null;
+ ActiveDirectoryGroup group2 = null;
+ try {
+ // Create an AD user.
+ user = azureResourceManager.accessManagement()
+ .activeDirectoryUsers()
+ .define(userName)
+ .withEmailAlias(userEmail)
+ .withPassword(SampleUtils.password())
+ .create();
+
+ // Assign the Reader role to the user, then revoke it.
+ RoleAssignment roleAssignment1 = azureResourceManager.accessManagement()
+ .roleAssignments()
+ .define(raName1)
+ .forUser(user)
+ .withBuiltInRole(BuiltInRole.READER)
+ .withSubscriptionScope(subscriptionId)
+ .create();
+ azureResourceManager.accessManagement().roleAssignments().deleteById(roleAssignment1.id());
+
+ // Get a role definition by scope and role name.
+ RoleDefinition roleDefinition = azureResourceManager.accessManagement()
+ .roleDefinitions()
+ .getByScopeAndRoleName("subscriptions/" + subscriptionId, "Contributor");
+ System.out.println("Contributor role definition id: " + roleDefinition.id());
+
+ // Create a service principal and assign the Contributor role to it.
+ sp = azureResourceManager.accessManagement()
+ .servicePrincipals()
+ .define(spName)
+ .withNewApplication()
+ .create();
+ // Wait for the service principal to propagate before assigning a role.
+ ResourceManagerUtils.sleep(Duration.ofSeconds(15));
+
+ spRoleAssignment = azureResourceManager.accessManagement()
+ .roleAssignments()
+ .define(raName2)
+ .forServicePrincipal(sp)
+ .withBuiltInRole(BuiltInRole.CONTRIBUTOR)
+ .withSubscriptionScope(subscriptionId)
+ .create();
+
+ // Create two Active Directory groups.
+ group1 = azureResourceManager.accessManagement()
+ .activeDirectoryGroups()
+ .define(groupName1)
+ .withEmailAlias(groupEmail1)
+ .create();
+ group2 = azureResourceManager.accessManagement()
+ .activeDirectoryGroups()
+ .define(groupName2)
+ .withEmailAlias(groupEmail2)
+ .create();
+
+ // Add the user, the service principal and the first group as members of the second group.
+ group2.update().withMember(user).withMember(sp).withMember(group1).apply();
+
+ System.out.println("Added user, service principal and group " + groupName1 + " to group " + groupName2);
+ return true;
+ } finally {
+ if (group2 != null) {
+ azureResourceManager.accessManagement().activeDirectoryGroups().deleteById(group2.id());
+ }
+ if (group1 != null) {
+ azureResourceManager.accessManagement().activeDirectoryGroups().deleteById(group1.id());
+ }
+ if (spRoleAssignment != null) {
+ azureResourceManager.accessManagement().roleAssignments().deleteById(spRoleAssignment.id());
+ }
+ if (sp != null) {
+ // withNewApplication() created an AD application named after the service principal; delete that
+ // application (which also deletes the service principal) so nothing is left orphaned. Guard the
+ // lookup because getByName(...) can return null under eventual consistency or a name collision.
+ ActiveDirectoryApplication application = azureResourceManager.accessManagement()
+ .activeDirectoryApplications()
+ .getByName(spName);
+ if (application != null) {
+ azureResourceManager.accessManagement().activeDirectoryApplications().deleteById(application.id());
+ }
+ }
+ if (user != null) {
+ azureResourceManager.accessManagement().activeDirectoryUsers().deleteById(user.id());
+ }
+ }
+ }
+
+ /**
+ * Main entry point.
+ *
+ * @param args the parameters
+ */
+ public static void main(String[] args) {
+ try {
+ final AzureProfile profile = new AzureProfile(AzureCloud.AZURE_PUBLIC_CLOUD);
+ final TokenCredential credential = new DefaultAzureCredentialBuilder()
+ .authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
+ .build();
+
+ AzureResourceManager azureResourceManager = AzureResourceManager.configure()
+ .withLogLevel(HttpLogDetailLevel.BASIC)
+ .authenticate(credential, profile)
+ .withDefaultSubscription();
+
+ runSample(azureResourceManager);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private ManageUsersGroupsAndRoles() {
+ }
+}
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/package-info.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/package-info.java
new file mode 100644
index 000000000000..26c505b175e6
--- /dev/null
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/authorization/samples/package-info.java
@@ -0,0 +1,7 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+/**
+ * Microsoft Entra ID (Azure Active Directory) samples.
+ */
+package com.azure.resourcemanager.authorization.samples;
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/samples/SampleUtils.java b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/samples/SampleUtils.java
index 57211a96ddb9..ced67cad174b 100644
--- a/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/samples/SampleUtils.java
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/java/com/azure/resourcemanager/samples/SampleUtils.java
@@ -14,6 +14,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
+import java.util.UUID;
/**
* Minimal set of helpers shared by the resource manager samples.
@@ -45,6 +46,16 @@ public static String randomUuid(AzureResourceManager azure) {
return azure.resourceGroups().manager().internalContext().randomUuid();
}
+ /**
+ * Generates a strong password that satisfies Microsoft Entra ID complexity requirements.
+ *
+ * @return a strong password
+ */
+ public static String password() {
+ return "P@0" + Base64.getUrlEncoder().withoutPadding().encodeToString(UUID.randomUUID().toString().getBytes(
+ StandardCharsets.US_ASCII)).substring(0, 12);
+ }
+
/**
* Generates an SSH public key in OpenSSH format.
*
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/resources/appservice/mi-blob-verify/package.json b/sdk/resourcemanager/azure-resourcemanager/src/samples/resources/appservice/mi-blob-verify/package.json
new file mode 100644
index 000000000000..c9677d98257b
--- /dev/null
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/resources/appservice/mi-blob-verify/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "mi-blob-verify",
+ "version": "1.0.0",
+ "main": "server.js",
+ "scripts": {
+ "start": "node server.js"
+ }
+}
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/samples/resources/appservice/mi-blob-verify/server.js b/sdk/resourcemanager/azure-resourcemanager/src/samples/resources/appservice/mi-blob-verify/server.js
new file mode 100644
index 000000000000..57fd182ccb9b
--- /dev/null
+++ b/sdk/resourcemanager/azure-resourcemanager/src/samples/resources/appservice/mi-blob-verify/server.js
@@ -0,0 +1,76 @@
+const http = require('http');
+const https = require('https');
+
+// The web app reaches Blob storage with its managed identity (no account key or connection string).
+// This verifier has no npm dependencies so it deploys without a build step: it fetches a token from the
+// App Service managed-identity endpoint and calls the Blob REST API directly.
+const endpoint = (process.env.STORAGE_BLOB_ENDPOINT || '').replace(/\/$/, '');
+const container = process.env.STORAGE_CONTAINER_NAME || 'verify';
+const apiVersion = '2021-08-06';
+
+function request(method, url, headers, body) {
+ return new Promise((resolve, reject) => {
+ const req = https.request(url, { method, headers }, res => {
+ const chunks = [];
+ res.on('data', d => chunks.push(d));
+ res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks).toString() }));
+ });
+ req.on('error', reject);
+ if (body) {
+ req.write(body);
+ }
+ req.end();
+ });
+}
+
+function getToken() {
+ // App Service injects IDENTITY_ENDPOINT / IDENTITY_HEADER for the managed identity.
+ const url = process.env.IDENTITY_ENDPOINT
+ + '?resource=https://storage.azure.com/&api-version=2019-08-01';
+ return new Promise((resolve, reject) => {
+ http.get(url, { headers: { 'X-IDENTITY-HEADER': process.env.IDENTITY_HEADER } }, res => {
+ const chunks = [];
+ res.on('data', d => chunks.push(d));
+ res.on('end', () => {
+ try {
+ resolve(JSON.parse(Buffer.concat(chunks).toString()).access_token);
+ } catch (e) {
+ reject(e);
+ }
+ });
+ }).on('error', reject);
+ });
+}
+
+async function verify() {
+ const token = await getToken();
+ const auth = { 'Authorization': 'Bearer ' + token, 'x-ms-version': apiVersion, 'x-ms-date': new Date().toUTCString() };
+ const content = 'hello-from-managed-identity';
+
+ // Create the container (ignore 409 Conflict if it already exists).
+ const create = await request('PUT', endpoint + '/' + container + '?restype=container', auth);
+ if (create.status !== 201 && create.status !== 409) {
+ throw new Error('create container failed: ' + create.status + ' ' + create.body);
+ }
+
+ // Upload and read back a blob using only the managed identity.
+ const blobUrl = endpoint + '/' + container + '/hello.txt';
+ const put = await request('PUT', blobUrl,
+ Object.assign({ 'x-ms-blob-type': 'BlockBlob', 'Content-Length': Buffer.byteLength(content) }, auth), content);
+ if (put.status !== 201) {
+ throw new Error('upload failed: ' + put.status + ' ' + put.body);
+ }
+ const get = await request('GET', blobUrl, auth);
+ return { ok: get.body === content, endpoint, container, read: get.body };
+}
+
+const server = http.createServer(async (req, res) => {
+ res.setHeader('content-type', 'application/json');
+ try {
+ res.end(JSON.stringify(await verify()));
+ } catch (e) {
+ res.statusCode = 500;
+ res.end(JSON.stringify({ ok: false, error: e.message }));
+ }
+});
+server.listen(process.env.PORT || 8080, () => console.log('listening'));
diff --git a/sdk/resourcemanager/azure-resourcemanager/src/test/java/com/azure/resourcemanager/samples/GraphRbacSampleTests.java b/sdk/resourcemanager/azure-resourcemanager/src/test/java/com/azure/resourcemanager/samples/GraphRbacSampleTests.java
new file mode 100644
index 000000000000..02d7573de846
--- /dev/null
+++ b/sdk/resourcemanager/azure-resourcemanager/src/test/java/com/azure/resourcemanager/samples/GraphRbacSampleTests.java
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.resourcemanager.samples;
+
+import com.azure.core.http.HttpPipeline;
+import com.azure.core.management.profile.AzureProfile;
+import com.azure.core.test.annotation.LiveOnly;
+import com.azure.resourcemanager.authorization.samples.ManageServicePrincipal;
+import com.azure.resourcemanager.authorization.samples.ManageUsersGroupsAndRoles;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Microsoft Entra ID (Graph/RBAC) samples run live-only: they create users, groups, applications and service
+ * principals whose secrets (passwords, client secrets, tokens) must never be persisted in a recording, and the
+ * service-principal re-authentication cannot be played back through the test proxy.
+ */
+public class GraphRbacSampleTests extends SamplesTestBase {
+
+ private AzureProfile profile;
+
+ @Test
+ @LiveOnly
+ public void testManageUsersGroupsAndRoles() {
+ Assertions.assertTrue(ManageUsersGroupsAndRoles.runSample(azureResourceManager));
+ }
+
+ @Test
+ @LiveOnly
+ public void testManageServicePrincipal() {
+ Assertions.assertTrue(ManageServicePrincipal.runSample(azureResourceManager, profile));
+ }
+
+ @Override
+ protected void initializeClients(HttpPipeline httpPipeline, AzureProfile profile) {
+ super.initializeClients(httpPipeline, profile);
+ this.profile = profile;
+ }
+}