diff --git a/sdk/sql/azure-resourcemanager-sql/CHANGELOG.md b/sdk/sql/azure-resourcemanager-sql/CHANGELOG.md
index 3f8b4a7113bd..d9186f7887b6 100644
--- a/sdk/sql/azure-resourcemanager-sql/CHANGELOG.md
+++ b/sdk/sql/azure-resourcemanager-sql/CHANGELOG.md
@@ -4,6 +4,9 @@
### Features Added
+- Supported `withAzureActiveDirectoryOnlyAuthentication` and `withExternalActiveDirectoryAdministrator` for `SqlServer`.
+- Supported `withManagedIdentity` for `SqlDatabase` import and export.
+
### Breaking Changes
### Bugs Fixed
@@ -363,21 +366,21 @@
### Breaking Changes
-- Removed `NEW` from `SecurityAlertPolicyState`. The constant is non-functional.
+- Removed `NEW` from `SecurityAlertPolicyState`. The constant is non-functional.
- Removed `withPolicyNew` method from `SqlDatabaseThreatDetectionPolicy` since `NEW` is no longer supported in `SecurityAlertPolicyState`.
- Removed `nextResetTime` and `resourceName` methods from `ServerMetric` and `SqlDatabase`. The methods are non-functional.
- Removed `listMetricsDefinitions` and `listMetrics` methods from `SqlDatabase`. Metrics in SQL have been replaced by the Azure monitor shoebox metrics API. Not in SQL any more.
- Removed `listServiceTierAdvisors` method from `SqlDatabase`. It's no longer supported.
- Removed class `ElasticPoolDatabaseActivity`. It's removed from service definition.
-- Removed `listDatabaseActivities`, `listDatabaseMetricDefinitions` and `listDatabaseMetrics` methods from `SqlElasticPool`.
-- Removed `elasticPoolName` and `serviceLevelObjective` methods from `SqlRestorableDroppedDatabase`.
+- Removed `listDatabaseActivities`, `listDatabaseMetricDefinitions` and `listDatabaseMetrics` methods from `SqlElasticPool`.
+- Removed `elasticPoolName` and `serviceLevelObjective` methods from `SqlRestorableDroppedDatabase`.
- Removed `getServiceObjective`, `listRecommendedElasticPools`, `listServiceObjectives` methods from `SqlServer`.
- Removed `withCreationDate` and `withThumbprint` from SqlServerKeyOperations. The properties are no longer mutable.
- Renamed class from `TransparentDataEncryptionInner` to `LogicalDatabaseTransparentDataEncryptionInner`.
- Removed class `TransparentDataEncryptionActivity`. The class is no longer functional.
- Removed `listActivities` from `TransparentDataEncryption` since `TransparentDataEncryptionActivity` is removed.
- Renamed `TransparentDataEncryptionStatus` to `TransparentDataEncryptionState`.
-- Removed `location`, `requestedDatabaseDtuCap`, `requestedDatabaseDtuGuarantee`, `requestedDatabaseDtuMax`, `requestedDatabaseDtuMin`,
+- Removed `location`, `requestedDatabaseDtuCap`, `requestedDatabaseDtuGuarantee`, `requestedDatabaseDtuMax`, `requestedDatabaseDtuMin`,
`requestedDtu`, `requestedDtuGuarantee` and `requestedElasticPoolName`, `requestedStorageLimitInGB` and `requestedStorageLimitInMB` methods from `ElasticPoolActivity`. The properties are no longer functional.
- Renamed class from `ElasticPoolActivityInner` to `ElasticPoolOperationInner`.
- Removed `readReplicaCount` and `withReadReplicaCount` from `DatabaseUpdate`. The property is non-functional.
diff --git a/sdk/sql/azure-resourcemanager-sql/assets.json b/sdk/sql/azure-resourcemanager-sql/assets.json
index 74882f72acc8..ac2251c2df50 100644
--- a/sdk/sql/azure-resourcemanager-sql/assets.json
+++ b/sdk/sql/azure-resourcemanager-sql/assets.json
@@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/sql/azure-resourcemanager-sql",
- "Tag": "java/sql/azure-resourcemanager-sql_2dccdeacd3"
+ "Tag": "java/sql/azure-resourcemanager-sql_a84e1b066f"
}
diff --git a/sdk/sql/azure-resourcemanager-sql/pom.xml b/sdk/sql/azure-resourcemanager-sql/pom.xml
index 7a8efaf5b219..523e118f5156 100644
--- a/sdk/sql/azure-resourcemanager-sql/pom.xml
+++ b/sdk/sql/azure-resourcemanager-sql/pom.xml
@@ -41,6 +41,9 @@
0.10
+ --add-reads com.azure.resourcemanager.sql=com.azure.resourcemanager.authorization
+ --add-reads com.azure.resourcemanager.sql=com.azure.resourcemanager.msi
+
--add-opens com.azure.resourcemanager.sql/com.azure.resourcemanager.sql=ALL-UNNAMED
--add-opens com.azure.resourcemanager.resources/com.azure.resourcemanager.resources=ALL-UNNAMED
--add-opens com.azure.resourcemanager.resources/com.azure.resourcemanager.resources.fluentcore.arm=ALL-UNNAMED
@@ -70,6 +73,18 @@
azure-resourcemanager-storage
2.55.5
+
+ com.azure.resourcemanager
+ azure-resourcemanager-msi
+ 2.53.8
+ test
+
+
+ com.azure.resourcemanager
+ azure-resourcemanager-authorization
+ 2.53.9
+ test
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseExportRequestImpl.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseExportRequestImpl.java
index 55f1c3eb6992..b6d1dfadb82c 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseExportRequestImpl.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseExportRequestImpl.java
@@ -3,6 +3,7 @@
package com.azure.resourcemanager.sql.implementation;
import com.azure.core.management.exception.ManagementException;
+import com.azure.core.util.CoreUtils;
import com.azure.resourcemanager.resources.fluentcore.dag.FunctionalTaskItem;
import com.azure.resourcemanager.resources.fluentcore.model.Creatable;
import com.azure.resourcemanager.resources.fluentcore.model.Indexable;
@@ -66,26 +67,37 @@ public SqlDatabaseExportRequestImpl exportTo(String storageUri) {
private Mono getOrCreateStorageAccountContainer(final StorageAccount storageAccount,
final String containerName, final String fileName, final FunctionalTaskItem.Context context) {
final SqlDatabaseExportRequestImpl self = this;
+ self.inner.withStorageUri(
+ String.format("%s%s/%s", storageAccount.endPoints().primary().blob(), containerName, fileName));
+ BlobContainers blobContainers = this.sqlServerManager.storageManager().blobContainers();
+ Mono container
+ = blobContainers.getAsync(parent().resourceGroupName(), storageAccount.name(), containerName)
+ .map(blobContainer -> (Indexable) blobContainer)
+ .onErrorResume(error -> {
+ if (error instanceof ManagementException) {
+ if (((ManagementException) error).getResponse().getStatusCode() == 404) {
+ return blobContainers.defineContainer(containerName)
+ .withExistingStorageAccount(parent().resourceGroupName(), storageAccount.name())
+ .withPublicAccess(PublicAccess.NONE)
+ .createAsync()
+ .map(blobContainer -> (Indexable) blobContainer);
+ }
+ }
+ return Mono.error(error);
+ });
+
+ if (!storageAccount.isSharedKeyAccessAllowed()
+ // self.inner.storageKey could be set before, e.g. with managed identity ID
+ || !CoreUtils.isNullOrEmpty(self.inner.storageKey())) {
+ return container;
+ }
+
return storageAccount.getKeysAsync()
.flatMap(storageAccountKeys -> Mono.justOrEmpty(storageAccountKeys.stream().findFirst()))
.flatMap(storageAccountKey -> {
- self.inner.withStorageUri(
- String.format("%s%s/%s", storageAccount.endPoints().primary().blob(), containerName, fileName));
self.inner.withStorageKeyType(StorageKeyType.STORAGE_ACCESS_KEY);
self.inner.withStorageKey(storageAccountKey.value());
- BlobContainers blobContainers = this.sqlServerManager.storageManager().blobContainers();
- return blobContainers.getAsync(parent().resourceGroupName(), storageAccount.name(), containerName)
- .onErrorResume(error -> {
- if (error instanceof ManagementException) {
- if (((ManagementException) error).getResponse().getStatusCode() == 404) {
- return blobContainers.defineContainer(containerName)
- .withExistingStorageAccount(parent().resourceGroupName(), storageAccount.name())
- .withPublicAccess(PublicAccess.NONE)
- .createAsync();
- }
- }
- return Mono.error(error);
- });
+ return container;
});
}
@@ -158,6 +170,20 @@ public SqlDatabaseExportRequestImpl withActiveDirectoryLoginAndPassword(String a
return this.withLoginAndPassword(administratorLogin, administratorPassword);
}
+ @Override
+ public SqlDatabaseExportRequestImpl withManagedIdentity(String managedIdentityResourceId) {
+ Objects.requireNonNull(managedIdentityResourceId);
+ this.inner.withAuthenticationType(AuthenticationType.MANAGED_IDENTITY.toString());
+ this.inner.withAdministratorLogin(managedIdentityResourceId);
+ // No administrator password is required for managed identity authentication.
+ this.inner.withAdministratorLoginPassword(null);
+
+ // Use the same MI for storage account access.
+ this.inner.withStorageKeyType(StorageKeyType.MANAGED_IDENTITY);
+ this.inner.withStorageKey(managedIdentityResourceId);
+ return this;
+ }
+
SqlDatabaseExportRequestImpl withLoginAndPassword(String administratorLogin, String administratorPassword) {
this.inner.withAdministratorLogin(administratorLogin);
this.inner.withAdministratorLoginPassword(administratorPassword);
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseForElasticPoolImpl.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseForElasticPoolImpl.java
index 301e9aeb3fc1..5ad5ce0c85e4 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseForElasticPoolImpl.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseForElasticPoolImpl.java
@@ -108,6 +108,12 @@ public SqlDatabaseForElasticPoolImpl withActiveDirectoryLoginAndPassword(String
return this;
}
+ @Override
+ public SqlDatabaseForElasticPoolImpl withManagedIdentity(String managedIdentityResourceId) {
+ this.sqlDatabase.withManagedIdentity(managedIdentityResourceId);
+ return this;
+ }
+
@Override
public SqlDatabaseForElasticPoolImpl fromRestorePoint(RestorePoint restorePoint) {
this.sqlDatabase.fromRestorePoint(restorePoint);
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseImpl.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseImpl.java
index 075702b38c3b..74fdc4b5379e 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseImpl.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseImpl.java
@@ -6,6 +6,7 @@
import com.azure.core.http.rest.PagedFlux;
import com.azure.core.http.rest.PagedIterable;
import com.azure.core.management.Region;
+import com.azure.core.util.CoreUtils;
import com.azure.resourcemanager.resources.fluentcore.arm.ResourceId;
import com.azure.resourcemanager.resources.fluentcore.arm.ResourceUtils;
import com.azure.resourcemanager.resources.fluentcore.arm.models.implementation.ExternalChildResourceImpl;
@@ -717,8 +718,11 @@ public SqlDatabaseImpl importFrom(final StorageAccount storageAccount, final Str
.flatMap(storageAccountKey -> {
self.importRequestInner.withStorageUri(
String.format("%s%s/%s", storageAccount.endPoints().primary().blob(), containerName, fileName));
- self.importRequestInner.withStorageKeyType(StorageKeyType.STORAGE_ACCESS_KEY);
- self.importRequestInner.withStorageKey(storageAccountKey.value());
+ if (storageAccount.isSharedKeyAccessAllowed()
+ && CoreUtils.isNullOrEmpty(self.importRequestInner.storageKey())) {
+ self.importRequestInner.withStorageKeyType(StorageKeyType.STORAGE_ACCESS_KEY);
+ self.importRequestInner.withStorageKey(storageAccountKey.value());
+ }
return context.voidMono();
}));
return this;
@@ -756,6 +760,20 @@ public SqlDatabaseImpl withActiveDirectoryLoginAndPassword(String administratorL
return this;
}
+ @Override
+ public SqlDatabaseImpl withManagedIdentity(String managedIdentityResourceId) {
+ Objects.requireNonNull(managedIdentityResourceId);
+ this.importRequestInner.withAuthenticationType(AuthenticationType.MANAGED_IDENTITY.toString());
+ this.importRequestInner.withAdministratorLogin(managedIdentityResourceId);
+ // No administrator password is required for managed identity authentication.
+ this.importRequestInner.withAdministratorLoginPassword(null);
+
+ // Use the same MI for storage account access.
+ this.importRequestInner.withStorageKeyType(StorageKeyType.MANAGED_IDENTITY);
+ this.importRequestInner.withStorageKey(managedIdentityResourceId);
+ return this;
+ }
+
@Override
public SqlDatabaseImpl fromRestorePoint(RestorePoint restorePoint) {
return fromRestorePoint(restorePoint, restorePoint.earliestRestoreDate());
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseImportRequestImpl.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseImportRequestImpl.java
index 4865c33a2586..ad9c4a23f888 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseImportRequestImpl.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlDatabaseImportRequestImpl.java
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
package com.azure.resourcemanager.sql.implementation;
+import com.azure.core.util.CoreUtils;
import com.azure.resourcemanager.resources.fluentcore.dag.FunctionalTaskItem;
import com.azure.resourcemanager.resources.fluentcore.model.Indexable;
import com.azure.resourcemanager.resources.fluentcore.model.implementation.ExecutableImpl;
@@ -55,11 +56,18 @@ public Mono executeWorkAsync() {
private Mono getOrCreateStorageAccountContainer(final StorageAccount storageAccount,
final String containerName, final String fileName, final FunctionalTaskItem.Context context) {
final SqlDatabaseImportRequestImpl self = this;
+ self.inner.withStorageUri(
+ String.format("%s%s/%s", storageAccount.endPoints().primary().blob(), containerName, fileName));
+
+ if (!storageAccount.isSharedKeyAccessAllowed()
+ // self.inner.storageKey could be set before, e.g. with managed identity ID
+ || !CoreUtils.isNullOrEmpty(self.inner.storageKey())) {
+ return context.voidMono();
+ }
+
return storageAccount.getKeysAsync()
.flatMap(storageAccountKeys -> Mono.justOrEmpty(storageAccountKeys.stream().findFirst()))
.flatMap(storageAccountKey -> {
- self.inner.withStorageUri(
- String.format("%s%s/%s", storageAccount.endPoints().primary().blob(), containerName, fileName));
self.inner.withStorageKeyType(StorageKeyType.STORAGE_ACCESS_KEY);
self.inner.withStorageKey(storageAccountKey.value());
return context.voidMono();
@@ -117,6 +125,20 @@ public SqlDatabaseImportRequestImpl withActiveDirectoryLoginAndPassword(String a
return this.withLoginAndPassword(administratorLogin, administratorPassword);
}
+ @Override
+ public SqlDatabaseImportRequestImpl withManagedIdentity(String managedIdentityResourceId) {
+ Objects.requireNonNull(managedIdentityResourceId);
+ this.inner.withAuthenticationType(AuthenticationType.MANAGED_IDENTITY.toString());
+ this.inner.withAdministratorLogin(managedIdentityResourceId);
+ // No administrator password is required for managed identity authentication.
+ this.inner.withAdministratorLoginPassword(null);
+
+ // Use the same MI for storage account access.
+ this.inner.withStorageKeyType(StorageKeyType.MANAGED_IDENTITY);
+ this.inner.withStorageKey(managedIdentityResourceId);
+ return this;
+ }
+
SqlDatabaseImportRequestImpl withLoginAndPassword(String administratorLogin, String administratorPassword) {
this.inner.withAdministratorLogin(administratorLogin);
this.inner.withAdministratorLoginPassword(administratorPassword);
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlServerImpl.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlServerImpl.java
index dd6a86864e17..927f72ecd469 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlServerImpl.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/implementation/SqlServerImpl.java
@@ -12,6 +12,7 @@
import com.azure.resourcemanager.resources.fluentcore.arm.models.implementation.GroupableResourceImpl;
import com.azure.resourcemanager.resources.fluentcore.dag.FunctionalTaskItem;
import com.azure.resourcemanager.resources.fluentcore.utils.PagedConverter;
+import com.azure.resourcemanager.resources.fluentcore.utils.ResourceManagerUtils;
import com.azure.resourcemanager.sql.SqlServerManager;
import com.azure.resourcemanager.sql.fluent.models.RestorableDroppedDatabaseInner;
import com.azure.resourcemanager.sql.fluent.models.ServerAutomaticTuningInner;
@@ -21,8 +22,11 @@
import com.azure.resourcemanager.sql.models.AdministratorName;
import com.azure.resourcemanager.sql.models.AdministratorType;
import com.azure.resourcemanager.sql.models.IdentityType;
+import com.azure.resourcemanager.sql.models.PrincipalType;
import com.azure.resourcemanager.sql.models.ResourceIdentity;
+import com.azure.resourcemanager.sql.models.ServerExternalAdministrator;
import com.azure.resourcemanager.sql.models.ServerMetric;
+import com.azure.resourcemanager.sql.models.ServerNetworkAccessFlag;
import com.azure.resourcemanager.sql.models.SqlDatabaseOperations;
import com.azure.resourcemanager.sql.models.SqlElasticPoolOperations;
import com.azure.resourcemanager.sql.models.SqlEncryptionProtectorOperations;
@@ -31,18 +35,20 @@
import com.azure.resourcemanager.sql.models.SqlFirewallRuleOperations;
import com.azure.resourcemanager.sql.models.SqlRestorableDroppedDatabase;
import com.azure.resourcemanager.sql.models.SqlServer;
-import com.azure.resourcemanager.sql.models.ServerNetworkAccessFlag;
import com.azure.resourcemanager.sql.models.SqlServerAutomaticTuning;
import com.azure.resourcemanager.sql.models.SqlServerDnsAliasOperations;
import com.azure.resourcemanager.sql.models.SqlServerKeyOperations;
import com.azure.resourcemanager.sql.models.SqlServerSecurityAlertPolicyOperations;
import com.azure.resourcemanager.sql.models.SqlVirtualNetworkRule;
import com.azure.resourcemanager.sql.models.SqlVirtualNetworkRuleOperations;
+import com.azure.resourcemanager.sql.models.UserIdentity;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Objects;
import java.util.UUID;
/** Implementation for SqlServer and its parent interfaces. */
@@ -321,6 +327,12 @@ public void removeActiveDirectoryAdministrator() {
.block();
}
+ @Override
+ public boolean isAzureActiveDirectoryOnlyAuthenticationEnabled() {
+ return this.innerModel().administrators() != null
+ && ResourceManagerUtils.toPrimitiveBoolean(this.innerModel().administrators().azureADOnlyAuthentication());
+ }
+
@Override
public SqlServerAutomaticTuning getServerAutomaticTuning() {
ServerAutomaticTuningInner serverAutomaticTuningInner
@@ -358,6 +370,37 @@ public SqlServerImpl withAdministratorPassword(String administratorLoginPassword
return this;
}
+ @Override
+ public SqlServerImpl withAzureActiveDirectoryOnlyAuthentication() {
+ if (this.innerModel().administrators() == null) {
+ this.innerModel()
+ .withAdministrators(
+ new ServerExternalAdministrator().withAdministratorType(AdministratorType.ACTIVE_DIRECTORY)
+ .withTenantId(UUID.fromString(this.manager().tenantId())));
+ }
+ this.innerModel().administrators().withAzureADOnlyAuthentication(true);
+ return this;
+ }
+
+ @Override
+ public SqlServerImpl withExternalActiveDirectoryAdministrator(String adminLogin, String sid,
+ PrincipalType principalType) {
+ if (this.innerModel().administrators() == null) {
+ this.innerModel()
+ .withAdministrators(
+ new ServerExternalAdministrator().withAdministratorType(AdministratorType.ACTIVE_DIRECTORY)
+ .withTenantId(UUID.fromString(this.manager().tenantId())));
+ } else {
+ this.innerModel().administrators().withAdministratorType(AdministratorType.ACTIVE_DIRECTORY);
+ }
+ this.innerModel()
+ .administrators()
+ .withLogin(adminLogin)
+ .withSid(UUID.fromString(sid))
+ .withPrincipalType(principalType);
+ return this;
+ }
+
@Override
public SqlServerImpl withoutAccessFromAzureServices() {
allowAzureServicesAccess = false;
@@ -484,10 +527,50 @@ public SqlServerImpl withoutDatabase(String databaseName) {
@Override
public SqlServerImpl withSystemAssignedManagedServiceIdentity() {
- this.innerModel().withIdentity(new ResourceIdentity().withType(IdentityType.SYSTEM_ASSIGNED));
+ initIdentity(IdentityType.SYSTEM_ASSIGNED);
+ return this;
+ }
+
+ @Override
+ public SqlServerImpl withPrimaryUserAssignedManagedServiceIdentity(String identityResourceId) {
+ Objects.requireNonNull(identityResourceId, "'identityResourceId' cannot be null.");
+ initIdentity(IdentityType.USER_ASSIGNED);
+ this.innerModel().identity().userAssignedIdentities().put(identityResourceId, new UserIdentity());
+ this.innerModel().withPrimaryUserAssignedIdentityId(identityResourceId);
return this;
}
+ /**
+ * Initializes or merges the identity on the inner model so that the requested
+ * {@code desiredType} is present. When both SYSTEM_ASSIGNED and USER_ASSIGNED
+ * are requested (in any order), the resulting type is SYSTEM_ASSIGNED_USER_ASSIGNED.
+ * Existing user-assigned identities are preserved across calls.
+ */
+ private void initIdentity(IdentityType desiredType) {
+ ResourceIdentity existing = this.innerModel().identity();
+ if (existing == null || existing.type() == null || existing.type() == IdentityType.NONE) {
+ ResourceIdentity identity = new ResourceIdentity().withType(desiredType);
+ if (desiredType == IdentityType.USER_ASSIGNED
+ || desiredType == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED) {
+ identity.withUserAssignedIdentities(new HashMap<>());
+ }
+ this.innerModel().withIdentity(identity);
+ } else if (existing.type() == desiredType || existing.type() == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED) {
+ // Already has the desired type (or already combined) — nothing to change.
+ if (existing.userAssignedIdentities() == null
+ && (existing.type() == IdentityType.USER_ASSIGNED
+ || existing.type() == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED)) {
+ existing.withUserAssignedIdentities(new HashMap<>());
+ }
+ } else {
+ // One of SYSTEM_ASSIGNED + USER_ASSIGNED → combine
+ existing.withType(IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED);
+ if (existing.userAssignedIdentities() == null) {
+ existing.withUserAssignedIdentities(new HashMap<>());
+ }
+ }
+ }
+
@Override
public SqlServerImpl enablePublicNetworkAccess() {
this.innerModel().withPublicNetworkAccess(ServerNetworkAccessFlag.ENABLED);
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/AuthenticationType.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/AuthenticationType.java
index dce6c2bd6f67..4ae7d2de471d 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/AuthenticationType.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/AuthenticationType.java
@@ -9,7 +9,10 @@ public enum AuthenticationType {
SQL("SQL"),
/** Enum value ADPassword. */
- ADPASSWORD("ADPassword");
+ ADPASSWORD("ADPassword"),
+
+ /** Enum value ManagedIdentity. */
+ MANAGED_IDENTITY("ManagedIdentity");
/** The actual serialized value for a AuthenticationType instance. */
private final String value;
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabase.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabase.java
index c45a29cf3756..ba9d730a1a55 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabase.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabase.java
@@ -503,6 +503,26 @@ interface WithAuthentication {
*/
SqlDatabase.DefinitionStages.WithAttachAllOptions
withActiveDirectoryLoginAndPassword(String administratorLogin, String administratorPassword);
+
+ /**
+ * Specifies the user-assigned managed identity (UAMI) used to authenticate to the SQL database for import.
+ *
+ * The SQL server must have the specified UAMI assigned (and typically set as its primary identity), the
+ * UAMI must be granted the appropriate role on the source storage account (e.g. {@code Storage Blob Data
+ * Contributor}), and it must be mapped to a database user with the required privileges. When this method
+ * is used, no administrator password is sent to the service.
+ *
+ * This method is for user-assigned managed identity. To use the server's system-assigned managed
+ * identity, enable SAMI on the server and pass the SAMI's resource identifier as well. The signed-in
+ * user's object ID is not a managed identity and cannot be used here.
+ *
+ * @param managedIdentityResourceId the Azure resource ID of the user-assigned managed identity to use
+ * @return next definition stage
+ */
+ default SqlDatabase.DefinitionStages.WithAttachAllOptions
+ withManagedIdentity(String managedIdentityResourceId) {
+ throw new UnsupportedOperationException("[withManagedIdentity] is not supported in " + getClass());
+ }
}
/**
@@ -581,6 +601,26 @@ interface WithAuthenticationAfterElasticPool {
*/
SqlDatabase.DefinitionStages.WithAttachFinal
withActiveDirectoryLoginAndPassword(String administratorLogin, String administratorPassword);
+
+ /**
+ * Specifies the user-assigned managed identity (UAMI) used to authenticate to the SQL database for import.
+ *
+ * The SQL server must have the specified UAMI assigned (and typically set as its primary identity), the
+ * UAMI must be granted the appropriate role on the source storage account (e.g. {@code Storage Blob Data
+ * Contributor}), and it must be mapped to a database user with the required privileges. When this method
+ * is used, no administrator password is sent to the service.
+ *
+ * This method is for user-assigned managed identity. To use the server's system-assigned managed
+ * identity, enable SAMI on the server and pass the SAMI's resource identifier as well. The signed-in
+ * user's object ID is not a managed identity and cannot be used here.
+ *
+ * @param managedIdentityResourceId the Azure resource ID of the user-assigned managed identity to use
+ * @return next definition stage
+ */
+ default SqlDatabase.DefinitionStages.WithAttachFinal
+ withManagedIdentity(String managedIdentityResourceId) {
+ throw new UnsupportedOperationException("[withManagedIdentity] is not supported in " + getClass());
+ }
}
/**
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseExportRequest.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseExportRequest.java
index fd40a3b16d64..0a4d71cdcc57 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseExportRequest.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseExportRequest.java
@@ -97,6 +97,26 @@ interface WithAuthenticationTypeAndLoginPassword {
*/
SqlDatabaseExportRequest.DefinitionStages.WithExecute
withActiveDirectoryLoginAndPassword(String administratorLogin, String administratorPassword);
+
+ /**
+ * Sets the user-assigned managed identity (UAMI) used to authenticate to the SQL database for export.
+ *
+ * The SQL server must have the specified UAMI assigned (and typically set as its primary identity), the
+ * UAMI must be granted the appropriate role on the target storage account (e.g. {@code Storage Blob Data
+ * Contributor}), and it must be mapped to a database user with the required privileges. When this method
+ * is used, no administrator password is sent to the service.
+ *
+ * This method is for user-assigned managed identity. System-assigned managed identity is not supported.
+ * See Limitations
+ *
+ * @param managedIdentityResourceId the Azure resource ID of the user-assigned managed identity to use
+ * for both SQL and storage access
+ * @return next definition stage
+ */
+ default SqlDatabaseExportRequest.DefinitionStages.WithExecute
+ withManagedIdentity(String managedIdentityResourceId) {
+ throw new UnsupportedOperationException("[withManagedIdentity] is not supported in " + getClass());
+ }
}
/**
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseImportRequest.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseImportRequest.java
index 21eac792c766..356b4baaa732 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseImportRequest.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseImportRequest.java
@@ -86,6 +86,26 @@ interface WithAuthenticationTypeAndLoginPassword {
*/
SqlDatabaseImportRequest.DefinitionStages.WithExecute
withActiveDirectoryLoginAndPassword(String administratorLogin, String administratorPassword);
+
+ /**
+ * Specifies the user-assigned managed identity (UAMI) used to authenticate to the SQL database for import.
+ *
+ * The SQL server must have the specified UAMI assigned (and typically set as its primary identity), the
+ * UAMI must be granted the appropriate role on the source storage account (e.g. {@code Storage Blob Data
+ * Contributor}), and it must be mapped to a database user with the required privileges. When this method
+ * is used, no administrator password is sent to the service.
+ *
+ * This method is for user-assigned managed identity. System-assigned managed identity is not supported.
+ * See Limitations
+ *
+ * @param managedIdentityResourceId the Azure resource ID of the user-assigned managed identity to use
+ * for both SQL and storage access
+ * @return next definition stage
+ */
+ default SqlDatabaseImportRequest.DefinitionStages.WithExecute
+ withManagedIdentity(String managedIdentityResourceId) {
+ throw new UnsupportedOperationException("[withManagedIdentity] is not supported in " + getClass());
+ }
}
/**
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseOperations.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseOperations.java
index 75edc555b91a..a1dd28fa1016 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseOperations.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlDatabaseOperations.java
@@ -187,6 +187,26 @@ interface WithAuthentication {
*/
SqlDatabaseOperations.DefinitionStages.WithEditionDefaults
withActiveDirectoryLoginAndPassword(String administratorLogin, String administratorPassword);
+
+ /**
+ * Specifies the user-assigned managed identity (UAMI) used to authenticate to the SQL database for import.
+ *
+ * The SQL server must have the specified UAMI assigned (and typically set as its primary identity), the
+ * UAMI must be granted the appropriate role on the source storage account (e.g. {@code Storage Blob Data
+ * Contributor}), and it must be mapped to a database user with the required privileges. When this method
+ * is used, no administrator password is sent to the service.
+ *
+ * This method is for user-assigned managed identity. To use the server's system-assigned managed
+ * identity, enable SAMI on the server and pass the SAMI's resource identifier as well. The signed-in
+ * user's object ID is not a managed identity and cannot be used here.
+ *
+ * @param managedIdentityResourceId the Azure resource ID of the user-assigned managed identity to use
+ * @return next definition stage
+ */
+ default SqlDatabaseOperations.DefinitionStages.WithEditionDefaults
+ withManagedIdentity(String managedIdentityResourceId) {
+ throw new UnsupportedOperationException("[withManagedIdentity] is not supported in " + getClass());
+ }
}
/** The SQL Database definition to import a BACPAC file as the source database. */
@@ -253,6 +273,26 @@ interface WithAuthenticationAfterElasticPool {
*/
SqlDatabaseOperations.DefinitionStages.WithCreateAfterElasticPoolOptions
withActiveDirectoryLoginAndPassword(String administratorLogin, String administratorPassword);
+
+ /**
+ * Specifies the user-assigned managed identity (UAMI) used to authenticate to the SQL database for import.
+ *
+ * The SQL server must have the specified UAMI assigned (and typically set as its primary identity), the
+ * UAMI must be granted the appropriate role on the source storage account (e.g. {@code Storage Blob Data
+ * Contributor}), and it must be mapped to a database user with the required privileges. When this method
+ * is used, no administrator password is sent to the service.
+ *
+ * This method is for user-assigned managed identity. System-assigned managed identity is not supported.
+ * See Limitations
+ *
+ * @param managedIdentityResourceId the Azure resource ID of the user-assigned managed identity to use
+ * for both SQL and storage access
+ * @return next definition stage
+ */
+ default SqlDatabaseOperations.DefinitionStages.WithCreateAfterElasticPoolOptions
+ withManagedIdentity(String managedIdentityResourceId) {
+ throw new UnsupportedOperationException("[withManagedIdentity] is not supported in " + getClass());
+ }
}
/** The SQL Database definition to set a restorable dropped database as the source database. */
diff --git a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlServer.java b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlServer.java
index 64d5188526ac..b270e0bbd331 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlServer.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/main/java/com/azure/resourcemanager/sql/models/SqlServer.java
@@ -157,6 +157,16 @@ public interface SqlServer
/** Removes the Active Directory administrator from this server. */
void removeActiveDirectoryAdministrator();
+ /**
+ * Checks whether Azure Active Directory (AAD) only authentication enabled.
+ *
+ * @return true if Azure Active Directory (AAD) only authentication enabled
+ */
+ default boolean isAzureActiveDirectoryOnlyAuthenticationEnabled() {
+ throw new UnsupportedOperationException(
+ "[isAzureActiveDirectoryOnlyAuthenticationEnabled] is not supported in " + getClass());
+ }
+
/**
* Gets a SQL server automatic tuning state and options.
*
@@ -234,10 +244,9 @@ public interface SqlServer
**************************************************************/
/** Container interface for all the definitions that need to be implemented. */
- interface Definition
- extends DefinitionStages.Blank, DefinitionStages.WithGroup, DefinitionStages.WithAdministratorLogin,
- DefinitionStages.WithAdministratorPassword, DefinitionStages.WithElasticPool, DefinitionStages.WithDatabase,
- DefinitionStages.WithFirewallRule, DefinitionStages.WithPublicNetworkAccess, DefinitionStages.WithCreate {
+ interface Definition extends DefinitionStages.Blank, DefinitionStages.WithGroup,
+ DefinitionStages.WithAdministratorLogin, DefinitionStages.WithAdministratorPassword,
+ DefinitionStages.WithExternalActiveDirectoryAdministrator, DefinitionStages.WithCreate {
}
/** Grouping of all the storage account definition stages. */
@@ -251,7 +260,7 @@ interface WithGroup extends GroupableResource.DefinitionStages.WithGroupWhen Microsoft Entra-only authentication is enabled, the SQL authentication (login/password)
+ * administrator is not configured on the server. This is required when the target subscription or management
+ * group enforces a policy that mandates Microsoft Entra-only authentication on Azure SQL Server creation.
+ */
+ interface WithAzureActiveDirectoryOnlyAuthentication {
+ /**
+ * Enables Microsoft Entra (Azure Active Directory) only authentication on the SQL Server.
+ *
+ * An external Microsoft Entra administrator must be specified on the next stage.
+ *
+ * @return Next stage of the SQL Server definition
+ */
+ WithExternalActiveDirectoryAdministrator withAzureActiveDirectoryOnlyAuthentication();
+ }
+
+ /** A SQL Server definition stage setting the external Microsoft Entra administrator on the server. */
+ interface WithExternalActiveDirectoryAdministrator {
+ /**
+ * Sets the external Microsoft Entra (Azure Active Directory) administrator on the SQL Server.
+ *
+ * @param adminLogin the user, group, or application login name
+ * @param sid the user, group, or application object ID
+ * @param principalType the principal type (User, Group, or Application). Must be specified explicitly, since
+ * the service does not reliably infer it from the SID
+ * @return Next stage of the SQL Server definition
+ */
+ WithCreate withExternalActiveDirectoryAdministrator(String adminLogin, String sid,
+ PrincipalType principalType);
+ }
+
/** A SQL Server definition setting admin user password. */
interface WithAdministratorPassword {
/**
@@ -297,6 +339,22 @@ interface WithSystemAssignedManagedServiceIdentity {
WithCreate withSystemAssignedManagedServiceIdentity();
}
+ /** A SQL Server definition setting a user-assigned managed service identity as the primary identity. */
+ interface WithUserAssignedManagedServiceIdentity {
+ /**
+ * Sets the specified user-assigned managed identity (UAMI) on the SQL server and marks it as the
+ * primary identity. If a system-assigned identity is also enabled, the resulting identity type is
+ * {@code SystemAssigned,UserAssigned}; otherwise it is {@code UserAssigned}.
+ *
+ * @param identityResourceId the Azure resource ID of the user-assigned managed identity
+ * @return Next stage of the SQL Server definition
+ */
+ default WithCreate withPrimaryUserAssignedManagedServiceIdentity(String identityResourceId) {
+ throw new UnsupportedOperationException(
+ "[withPrimaryUserAssignedManagedServiceIdentity] is not supported in " + getClass());
+ }
+ }
+
/** A SQL Server definition for specifying elastic pool. */
interface WithElasticPool {
/**
@@ -366,8 +424,9 @@ interface WithPublicNetworkAccess {
* A SQL Server definition with sufficient inputs to create a new SQL Server in the cloud, but exposing
* additional optional inputs to specify.
*/
- interface WithCreate extends Creatable, WithActiveDirectoryAdministrator,
- WithSystemAssignedManagedServiceIdentity, WithElasticPool, WithDatabase, WithFirewallRule,
+ interface WithCreate
+ extends Creatable, WithActiveDirectoryAdministrator, WithSystemAssignedManagedServiceIdentity,
+ WithUserAssignedManagedServiceIdentity, WithElasticPool, WithDatabase, WithFirewallRule,
WithVirtualNetworkRule, WithPublicNetworkAccess, DefinitionWithTags {
}
}
diff --git a/sdk/sql/azure-resourcemanager-sql/src/test/java/com/azure/resourcemanager/sql/SqlServerOperationsTests.java b/sdk/sql/azure-resourcemanager-sql/src/test/java/com/azure/resourcemanager/sql/SqlServerOperationsTests.java
index e962324290bf..c8dadeb79038 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/test/java/com/azure/resourcemanager/sql/SqlServerOperationsTests.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/test/java/com/azure/resourcemanager/sql/SqlServerOperationsTests.java
@@ -7,6 +7,8 @@
import com.azure.core.management.Region;
import com.azure.core.management.exception.ManagementException;
import com.azure.core.test.annotation.DoNotRecord;
+import com.azure.resourcemanager.authorization.models.BuiltInRole;
+import com.azure.resourcemanager.msi.models.Identity;
import com.azure.resourcemanager.resources.fluentcore.model.Creatable;
import com.azure.resourcemanager.resources.fluentcore.model.Indexable;
import com.azure.resourcemanager.resources.fluentcore.utils.ResourceManagerUtils;
@@ -24,13 +26,14 @@
import com.azure.resourcemanager.sql.models.ElasticPoolEdition;
import com.azure.resourcemanager.sql.models.ElasticPoolSku;
import com.azure.resourcemanager.sql.models.FailoverGroupReplicationRole;
+import com.azure.resourcemanager.sql.models.IdentityType;
+import com.azure.resourcemanager.sql.models.PrincipalType;
import com.azure.resourcemanager.sql.models.ReadOnlyEndpointFailoverPolicy;
import com.azure.resourcemanager.sql.models.ReadWriteEndpointFailoverPolicy;
import com.azure.resourcemanager.sql.models.RegionCapabilities;
import com.azure.resourcemanager.sql.models.ReplicationLink;
+import com.azure.resourcemanager.sql.models.ReplicationState;
import com.azure.resourcemanager.sql.models.SampleName;
-import com.azure.resourcemanager.sql.models.SecurityAlertPolicyName;
-import com.azure.resourcemanager.sql.models.SecurityAlertPolicyState;
import com.azure.resourcemanager.sql.models.ServerNetworkAccessFlag;
import com.azure.resourcemanager.sql.models.ServiceObjectiveName;
import com.azure.resourcemanager.sql.models.Sku;
@@ -40,7 +43,6 @@
import com.azure.resourcemanager.sql.models.SqlDatabaseImportExportResponse;
import com.azure.resourcemanager.sql.models.SqlDatabasePremiumServiceObjective;
import com.azure.resourcemanager.sql.models.SqlDatabaseStandardServiceObjective;
-import com.azure.resourcemanager.sql.models.SqlDatabaseThreatDetectionPolicy;
import com.azure.resourcemanager.sql.models.SqlElasticPool;
import com.azure.resourcemanager.sql.models.SqlElasticPoolBasicEDTUs;
import com.azure.resourcemanager.sql.models.SqlFailoverGroup;
@@ -56,6 +58,7 @@
import com.azure.resourcemanager.sql.models.TransparentDataEncryption;
import com.azure.resourcemanager.sql.models.TransparentDataEncryptionState;
import com.azure.resourcemanager.storage.models.StorageAccount;
+import com.azure.resourcemanager.test.model.AzureUser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -85,6 +88,9 @@ public class SqlServerOperationsTests extends SqlServerTest {
private static final String SQL_FIREWALLRULE_NAME = "firewallrule1";
private static final String START_IPADDRESS = "10.102.1.10";
private static final String END_IPADDRESS = "10.102.1.12";
+ private static final Region DEFAULT_REGION = Region.US_WEST3;
+ private static final Region SECONDARY_REGION = Region.US_EAST2;
+ private static final Region TERTIARY_REGION = Region.EUROPE_NORTH;
// Only one sync database is allowed per region per subscription
// canCRUDSqlSyncMember and canCRUDSqlSyncGroup need to be in 2 different region
@@ -99,13 +105,14 @@ public void canCRUDSqlSyncMember() throws Exception {
final String administratorLogin = "sqladmin";
final String administratorPassword = password();
+ AzureUser user = azureCliSignedInUser();
// Create
SqlServer sqlPrimaryServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_WEST3)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(administratorLogin)
- .withAdministratorPassword(administratorPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.defineDatabase(dbName)
.fromSample(SampleName.ADVENTURE_WORKS_LT)
.withStandardEdition(SqlDatabaseStandardServiceObjective.S0)
@@ -122,6 +129,14 @@ public void canCRUDSqlSyncMember() throws Exception {
SqlDatabase dbSync = sqlPrimaryServer.databases().get(dbSyncName);
SqlDatabase dbMember = sqlPrimaryServer.databases().get(dbMemberName);
+ // SQL Data Sync requires SQL authentication for connections to the hub and member databases. Microsoft Entra (Azure AD)
+ // authentication isn't supported by SQL Data Sync. Because SQL authentication relies on static passwords,
+ // it doesn't benefit from modern protections like multifactor authentication (MFA), Conditional Access, or managed identities.
+ // This can increase exposure for the entire SQL instance to credential theft, brute‑force attacks, and operational
+ // overhead for password rotation and policy enforcement. Where possible, prefer solutions that support Microsoft
+ // Entra authentication or managed identities. Since SQL Data Sync is scheduled for retirement, migrate to an
+ // alternative that aligns with your organization's security standards.
+ // See https://learn.microsoft.com/azure/azure-sql/database/sql-data-sync-data-sql-server-sql-database?view=azuresql
SqlSyncGroup sqlSyncGroup = dbSync.syncGroups()
.define(syncGroupName)
.withSyncDatabaseId(dbSource.id())
@@ -169,13 +184,14 @@ public void canCRUDSqlSyncGroup() throws Exception {
final String administratorLogin = "sqladmin";
final String administratorPassword = password();
+ AzureUser user = azureCliSignedInUser();
// Create
SqlServer sqlPrimaryServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(SECONDARY_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(administratorLogin)
- .withAdministratorPassword(administratorPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.defineDatabase(dbName)
.fromSample(SampleName.ADVENTURE_WORKS_LT)
.withStandardEdition(SqlDatabaseStandardServiceObjective.S0)
@@ -188,6 +204,14 @@ public void canCRUDSqlSyncGroup() throws Exception {
SqlDatabase dbSource = sqlPrimaryServer.databases().get(dbName);
SqlDatabase dbSync = sqlPrimaryServer.databases().get(dbSyncName);
+ // SQL Data Sync requires SQL authentication for connections to the hub and member databases. Microsoft Entra (Azure AD)
+ // authentication isn't supported by SQL Data Sync. Because SQL authentication relies on static passwords,
+ // it doesn't benefit from modern protections like multifactor authentication (MFA), Conditional Access, or managed identities.
+ // This can increase exposure for the entire SQL instance to credential theft, brute‑force attacks, and operational
+ // overhead for password rotation and policy enforcement. Where possible, prefer solutions that support Microsoft
+ // Entra authentication or managed identities. Since SQL Data Sync is scheduled for retirement, migrate to an
+ // alternative that aligns with your organization's security standards.
+ // See https://learn.microsoft.com/azure/azure-sql/database/sql-data-sync-data-sql-server-sql-database?view=azuresql
SqlSyncGroup sqlSyncGroup = dbSync.syncGroups()
.define(syncGroupName)
.withSyncDatabaseId(dbSource.id())
@@ -204,7 +228,7 @@ public void canCRUDSqlSyncGroup() throws Exception {
Assertions.assertTrue(sqlServerManager.sqlServers()
.syncGroups()
- .listSyncDatabaseIds(Region.US_EAST)
+ .listSyncDatabaseIds(SECONDARY_REGION)
.stream()
.findAny()
.isPresent());
@@ -224,16 +248,15 @@ public void canCopySqlDatabase() throws Exception {
final String sqlSecondaryServerName = generateRandomResourceName("sqlsec", 22);
final String epName = "epSample";
final String dbName = "dbSample";
- final String administratorLogin = "sqladmin";
- final String administratorPassword = password();
+ AzureUser user = azureCliSignedInUser();
// Create
SqlServer sqlPrimaryServer = sqlServerManager.sqlServers()
.define(sqlPrimaryServerName)
- .withRegion(Region.US_EAST2)
+ .withRegion(SECONDARY_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(administratorLogin)
- .withAdministratorPassword(administratorPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.defineElasticPool(epName)
.withPremiumPool()
.attach()
@@ -245,10 +268,10 @@ public void canCopySqlDatabase() throws Exception {
SqlServer sqlSecondaryServer = sqlServerManager.sqlServers()
.define(sqlSecondaryServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withExistingResourceGroup(rgName)
- .withAdministratorLogin(administratorLogin)
- .withAdministratorPassword(administratorPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.create();
SqlDatabase dbSample = sqlPrimaryServer.databases().get(dbName);
@@ -271,16 +294,15 @@ public void canCRUDSqlFailoverGroup() throws Exception {
final String failoverGroupName = generateRandomResourceName("fg", 22);
final String failoverGroupName2 = generateRandomResourceName("fg2", 22);
final String dbName = "dbSample";
- final String administratorLogin = "sqladmin";
- final String administratorPassword = password();
+ AzureUser user = azureCliSignedInUser();
// Create
SqlServer sqlPrimaryServer = sqlServerManager.sqlServers()
.define(sqlPrimaryServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(administratorLogin)
- .withAdministratorPassword(administratorPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.defineDatabase(dbName)
.fromSample(SampleName.ADVENTURE_WORKS_LT)
.withStandardEdition(SqlDatabaseStandardServiceObjective.S0)
@@ -289,18 +311,18 @@ public void canCRUDSqlFailoverGroup() throws Exception {
SqlServer sqlSecondaryServer = sqlServerManager.sqlServers()
.define(sqlSecondaryServerName)
- .withRegion(Region.US_EAST2)
+ .withRegion(SECONDARY_REGION)
.withExistingResourceGroup(rgName)
- .withAdministratorLogin(administratorLogin)
- .withAdministratorPassword(administratorPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.create();
SqlServer sqlOtherServer = sqlServerManager.sqlServers()
.define(sqlOtherServerName)
- .withRegion(Region.US_SOUTH_CENTRAL)
+ .withRegion(TERTIARY_REGION)
.withExistingResourceGroup(rgName)
- .withAdministratorLogin(administratorLogin)
- .withAdministratorPassword(administratorPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.create();
SqlFailoverGroup failoverGroup = sqlPrimaryServer.failoverGroups()
@@ -391,19 +413,16 @@ public void canCRUDSqlFailoverGroup() throws Exception {
@Test
public void canChangeSqlServerAndDatabaseAutomaticTuning() throws Exception {
- String sqlServerAdminName = "sqladmin";
- String sqlServerAdminPassword = password();
String databaseName = "db-from-sample";
- String id = generateRandomUuid();
- String storageName = generateRandomResourceName(sqlServerName, 22);
+ AzureUser user = azureCliSignedInUser();
// Create
SqlServer sqlServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(sqlServerAdminName)
- .withAdministratorPassword(sqlServerAdminPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.defineDatabase(databaseName)
.fromSample(SampleName.ADVENTURE_WORKS_LT)
.withBasicEdition()
@@ -469,16 +488,15 @@ public void canChangeSqlServerAndDatabaseAutomaticTuning() throws Exception {
public void canCreateAndAquireServerDnsAlias() throws Exception {
String sqlServerName1 = sqlServerName + "1";
String sqlServerName2 = sqlServerName + "2";
- String sqlServerAdminName = "sqladmin";
- String sqlServerAdminPassword = password();
+ AzureUser user = azureCliSignedInUser();
// Create
SqlServer sqlServer1 = sqlServerManager.sqlServers()
.define(sqlServerName1)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(sqlServerAdminName)
- .withAdministratorPassword(sqlServerAdminPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.create();
Assertions.assertNotNull(sqlServer1);
@@ -497,10 +515,10 @@ public void canCreateAndAquireServerDnsAlias() throws Exception {
SqlServer sqlServer2 = sqlServerManager.sqlServers()
.define(sqlServerName2)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(sqlServerAdminName)
- .withAdministratorPassword(sqlServerAdminPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.create();
Assertions.assertNotNull(sqlServer2);
@@ -523,11 +541,10 @@ public void canCreateAndAquireServerDnsAlias() throws Exception {
@DoNotRecord(skipInPlayback = true)
public void canGetSqlServerCapabilitiesAndCreateIdentity() throws Exception {
// LiveOnly because "test timing out after latest test proxy update"
- String sqlServerAdminName = "sqladmin";
- String sqlServerAdminPassword = password();
String databaseName = "db-from-sample";
+ AzureUser user = azureCliSignedInUser();
- RegionCapabilities regionCapabilities = sqlServerManager.sqlServers().getCapabilitiesByRegion(Region.US_EAST);
+ RegionCapabilities regionCapabilities = sqlServerManager.sqlServers().getCapabilitiesByRegion(DEFAULT_REGION);
Assertions.assertNotNull(regionCapabilities);
Assertions.assertNotNull(regionCapabilities.supportedCapabilitiesByServerVersion().get("12.0"));
Assertions.assertTrue(
@@ -539,10 +556,10 @@ public void canGetSqlServerCapabilitiesAndCreateIdentity() throws Exception {
// Create
SqlServer sqlServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(sqlServerAdminName)
- .withAdministratorPassword(sqlServerAdminPassword)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.withSystemAssignedManagedServiceIdentity()
.defineDatabase(databaseName)
.fromSample(SampleName.ADVENTURE_WORKS_LT)
@@ -576,56 +593,60 @@ public void canGetSqlServerCapabilitiesAndCreateIdentity() throws Exception {
@Test
@DoNotRecord(skipInPlayback = true)
// The test makes calls to the Azure Storage data plane APIs which are not mocked at this time.
- public void canCRUDSqlServerWithImportDatabase() throws Exception {
- // Create
-
- String sqlServerAdminName = "sqladmin";
- String sqlServerAdminPassword = password();
- String id = generateRandomUuid();
+ public void canCRUDSqlServerWithImportDatabase() {
String storageName = generateRandomResourceName(sqlServerName, 22);
+ String uamiName = generateRandomResourceName("uami", 18);
+
+ resourceManager.resourceGroups().define(rgName).withRegion(DEFAULT_REGION).create();
+
+ Identity uami = msiManager.identities()
+ .define(uamiName)
+ .withRegion(DEFAULT_REGION)
+ .withExistingResourceGroup(rgName)
+ .create();
+
+ StorageAccount storageAccount = storageManager.storageAccounts()
+ .define(storageName)
+ .withRegion(DEFAULT_REGION)
+ .withExistingResourceGroup(rgName)
+ .disableSharedKeyAccess()
+ .create();
+
+ authorizationManager.roleAssignments()
+ .define(generateRandomUuid())
+ .forObjectId(uami.principalId())
+ .withBuiltInRole(BuiltInRole.STORAGE_BLOB_DATA_CONTRIBUTOR)
+ .withResourceScope(storageAccount)
+ .create();
+
+ ResourceManagerUtils.sleep(Duration.ofMinutes(1));
SqlServer sqlServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
- .withNewResourceGroup(rgName)
- .withAdministratorLogin(sqlServerAdminName)
- .withAdministratorPassword(sqlServerAdminPassword)
- .withActiveDirectoryAdministrator("DSEng", id)
+ .withRegion(DEFAULT_REGION)
+ .withExistingResourceGroup(rgName)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(uamiName, uami.principalId(), PrincipalType.APPLICATION)
+ .withPrimaryUserAssignedManagedServiceIdentity(uami.id())
+ .withSystemAssignedManagedServiceIdentity()
.create();
+ Assertions.assertTrue(sqlServer.isAzureActiveDirectoryOnlyAuthenticationEnabled());
+ Assertions.assertSame(IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED, sqlServer.managedServiceIdentityType());
+
SqlDatabase dbFromSample = sqlServer.databases()
.define("db-from-sample")
.fromSample(SampleName.ADVENTURE_WORKS_LT)
.withBasicEdition()
- .withTag("tag1", "value1")
.create();
Assertions.assertNotNull(dbFromSample);
Assertions.assertEquals(DatabaseEdition.BASIC, dbFromSample.edition());
- SqlDatabaseImportExportResponse exportedDB;
- StorageAccount storageAccount = null;
- try {
- storageAccount
- = storageManager.storageAccounts().getByResourceGroup(sqlServer.resourceGroupName(), storageName);
- } catch (ManagementException e) {
- Assertions.assertEquals(404, e.getResponse().getStatusCode());
- }
- if (storageAccount == null) {
- Creatable storageAccountCreatable = storageManager.storageAccounts()
- .define(storageName)
- .withRegion(sqlServer.regionName())
- .withExistingResourceGroup(sqlServer.resourceGroupName());
-
- exportedDB = dbFromSample.exportTo(storageAccountCreatable, "from-sample", "dbfromsample.bacpac")
- .withSqlAdministratorLoginAndPassword(sqlServerAdminName, sqlServerAdminPassword)
+ SqlDatabaseImportExportResponse exportedDB
+ = dbFromSample.exportTo(storageAccount, "from-sample", "dbfromsample.bacpac")
+ .withManagedIdentity(uami.id())
.execute();
- storageAccount
- = storageManager.storageAccounts().getByResourceGroup(sqlServer.resourceGroupName(), storageName);
- } else {
- exportedDB = dbFromSample.exportTo(storageAccount, "from-sample", "dbfromsample.bacpac")
- .withSqlAdministratorLoginAndPassword(sqlServerAdminName, sqlServerAdminPassword)
- .execute();
- }
+ Assertions.assertNotNull(exportedDB);
SqlDatabase dbFromImport = sqlServer.databases()
.define("db-from-import")
@@ -633,12 +654,24 @@ public void canCRUDSqlServerWithImportDatabase() throws Exception {
.withBasicPool()
.attach()
.importFrom(storageAccount, "from-sample", "dbfromsample.bacpac")
- .withSqlAdministratorLoginAndPassword(sqlServerAdminName, sqlServerAdminPassword)
+ .withManagedIdentity(uami.id())
.withTag("tag2", "value2")
.create();
Assertions.assertNotNull(dbFromImport);
Assertions.assertEquals("ep1", dbFromImport.elasticPoolName());
+ // Test importBacpac with managed identity on an existing empty database
+ SqlDatabase dbForImportBacpac
+ = sqlServer.databases().define("db-for-import-bacpac").withBasicEdition().create();
+ Assertions.assertNotNull(dbForImportBacpac);
+
+ SqlDatabaseImportExportResponse importBacpacResponse
+ = dbForImportBacpac.importBacpac(storageAccount, "from-sample", "dbfromsample.bacpac")
+ .withManagedIdentity(uami.id())
+ .execute();
+ Assertions.assertNotNull(importBacpacResponse);
+
+ dbForImportBacpac.delete();
dbFromImport.delete();
dbFromSample.delete();
sqlServer.elasticPools().delete("ep1");
@@ -648,28 +681,24 @@ public void canCRUDSqlServerWithImportDatabase() throws Exception {
@Test
public void canCRUDSqlServerWithFirewallRule() throws Exception {
// Create
- String sqlServerAdminName = "sqladmin";
- String id = azureCliSignedInUser().id();
+ AzureUser user = azureCliSignedInUser();
SqlServer sqlServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin(sqlServerAdminName)
- .withAdministratorPassword(password())
- .withActiveDirectoryAdministrator("DSEng", id)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator("DSEng", user.id(), PrincipalType.USER)
.withoutAccessFromAzureServices()
.defineFirewallRule("somefirewallrule1")
.withIpAddress("0.0.0.1")
.attach()
.withTag("tag1", "value1")
.create();
- Assertions.assertEquals(sqlServerAdminName, sqlServer.administratorLogin());
Assertions.assertEquals("v12.0", sqlServer.kind());
Assertions.assertEquals("12.0", sqlServer.version());
sqlServer = sqlServerManager.sqlServers().getByResourceGroup(rgName, sqlServerName);
- Assertions.assertEquals(sqlServerAdminName, sqlServer.administratorLogin());
Assertions.assertEquals("v12.0", sqlServer.kind());
Assertions.assertEquals("12.0", sqlServer.version());
@@ -679,15 +708,16 @@ public void canCRUDSqlServerWithFirewallRule() throws Exception {
Assertions.assertNotNull(sqlADAdmin.id());
Assertions.assertEquals(AdministratorType.ACTIVE_DIRECTORY, sqlADAdmin.administratorType());
- sqlADAdmin = sqlServer.setActiveDirectoryAdministrator("DSEngAll", id);
+ sqlADAdmin = sqlServer.setActiveDirectoryAdministrator("DSEngAll", user.id());
Assertions.assertNotNull(sqlADAdmin);
Assertions.assertEquals("DSEngAll", sqlADAdmin.signInName());
Assertions.assertNotNull(sqlADAdmin.id());
Assertions.assertEquals(AdministratorType.ACTIVE_DIRECTORY, sqlADAdmin.administratorType());
- sqlServer.removeActiveDirectoryAdministrator();
- final SqlServer finalSqlServer = sqlServer;
- validateResourceNotFound(() -> finalSqlServer.getActiveDirectoryAdministrator());
+ // Cannot remove AD admin when AzureADOnlyAuthentication is enabled.
+ // Verify it is still present instead.
+ sqlADAdmin = sqlServer.getActiveDirectoryAdministrator();
+ Assertions.assertNotNull(sqlADAdmin);
SqlFirewallRule firewallRule
= sqlServerManager.sqlServers().firewallRules().getBySqlServer(rgName, sqlServerName, "somefirewallrule1");
@@ -760,7 +790,11 @@ public void canCRUDSqlServer() throws Exception {
Assertions.assertEquals(CheckNameAvailabilityReason.ALREADY_EXISTS.toString(),
checkNameResult.unavailabilityReason());
- sqlServer.update().withAdministratorPassword("P@ssword~2").apply();
+ // Server is created with Microsoft Entra-only authentication, so SQL admin password update is not applicable.
+ // Exercise an update path using system-assigned managed identity instead.
+ sqlServer.update().withSystemAssignedManagedServiceIdentity().apply();
+ Assertions.assertTrue(sqlServer.isManagedServiceIdentityEnabled());
+ Assertions.assertNotNull(sqlServer.systemAssignedManagedServiceIdentityPrincipalId());
// List
PagedIterable sqlServers = sqlServerManager.sqlServers().listByResourceGroup(rgName);
@@ -787,14 +821,15 @@ public void canUseCoolShortcutsForResourceCreation() throws Exception {
String elasticPool2Name = "elasticPool2";
String elasticPool3Name = "elasticPool3";
String elasticPool1Name = SQL_ELASTIC_POOL_NAME;
+ AzureUser user = azureCliSignedInUser();
// Create
SqlServer sqlServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin("userName")
- .withAdministratorPassword("Password~1")
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.withoutAccessFromAzureServices()
.defineDatabase(SQL_DATABASE_NAME)
.attach()
@@ -893,9 +928,19 @@ public void canUseCoolShortcutsForResourceCreation() throws Exception {
}
@Test
- public void canCRUDSqlDatabase() throws Exception {
+ public void canCRUDSqlDatabase() {
+ AzureUser user = azureCliSignedInUser();
+
// Create
- SqlServer sqlServer = createSqlServer();
+ SqlServer sqlServer = sqlServerManager.sqlServers()
+ .define(sqlServerName)
+ .withRegion(DEFAULT_REGION)
+ .withNewResourceGroup(rgName)
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
+ .withSystemAssignedManagedServiceIdentity()
+ .create();
+
Mono resourceStream = sqlServer.databases()
.define(SQL_DATABASE_NAME)
.withStandardEdition(SqlDatabaseStandardServiceObjective.S0)
@@ -906,35 +951,42 @@ public void canCRUDSqlDatabase() throws Exception {
validateSqlDatabase(sqlDatabase, SQL_DATABASE_NAME);
Assertions.assertTrue(sqlServer.databases().list().size() > 0);
- // Test security alert policy settings.
-
- final String storageAccountName = generateRandomResourceName("sqlsa", 20);
- StorageAccount storageAccount = storageManager.storageAccounts()
- .define(storageAccountName)
- .withRegion(Region.US_EAST)
- .withExistingResourceGroup(rgName)
- .create();
- String accountKey = storageAccount.getKeys().get(0).value();
- String blobEntrypoint = storageAccount.endPoints().primary().blob();
-
- List disabledAlerts = Collections.singletonList("Sql_Injection");
-
- sqlDatabase.defineThreatDetectionPolicy(SecurityAlertPolicyName.fromString("myPolicy"))
- .withPolicyEnabled()
- .withStorageEndpoint(blobEntrypoint)
- .withStorageAccountAccessKey(accountKey)
- .withAlertsFilter(disabledAlerts)
- .create();
-
- sqlDatabase.refresh();
-
- SqlDatabaseThreatDetectionPolicy alertPolicy = sqlDatabase.getThreatDetectionPolicy();
- Assertions.assertNotNull(alertPolicy);
- Assertions.assertEquals(SecurityAlertPolicyState.ENABLED, alertPolicy.currentState());
- Assertions.assertEquals(alertPolicy.disabledAlertList(), disabledAlerts);
- Assertions.assertTrue(alertPolicy.isDefaultSecurityAlertPolicy());
-
- // Done testing security alert policy
+ // final String storageAccountName = generateRandomResourceName("sqlsa", 20);
+ // Legacy threat detection policy doesn't support MI. The new Advanced Threat Protection(ATP) does not require
+ // storage account any more. See Oury Ba's answer:
+ // https://learn.microsoft.com/answers/questions/2276392/how-to-create-microsoft-sql-servers-securityalertp
+ // StorageAccount storageAccount = storageManager.storageAccounts()
+ // .define(storageAccountName)
+ // .withRegion(DEFAULT_REGION)
+ // .withExistingResourceGroup(rgName)
+ // .disableSharedKeyAccess()
+ // .create();
+ //
+ // authorizationManager.roleAssignments()
+ // .define(generateRandomUuid())
+ // .forObjectId(sqlServer.systemAssignedManagedServiceIdentityPrincipalId())
+ // .withBuiltInRole(BuiltInRole.STORAGE_BLOB_DATA_CONTRIBUTOR)
+ // .withResourceScope(storageAccount)
+ // .create();
+ // String blobEntrypoint = storageAccount.endPoints().primary().blob();
+ //
+ // List disabledAlerts = Collections.singletonList("Sql_Injection");
+ //
+ // sqlDatabase.defineThreatDetectionPolicy(SecurityAlertPolicyName.fromString("myPolicy"))
+ // .withPolicyEnabled()
+ // .withStorageEndpoint(blobEntrypoint)
+ // // use system-assigned MI
+ // .withStorageAccountAccessKey(null)
+ // .withAlertsFilter(disabledAlerts)
+ // .create();
+ //
+ // sqlDatabase.refresh();
+ //
+ // SqlDatabaseThreatDetectionPolicy alertPolicy = sqlDatabase.getThreatDetectionPolicy();
+ // Assertions.assertNotNull(alertPolicy);
+ // Assertions.assertEquals(SecurityAlertPolicyState.ENABLED, alertPolicy.currentState());
+ // Assertions.assertEquals(alertPolicy.disabledAlertList(), disabledAlerts);
+ // Assertions.assertTrue(alertPolicy.isDefaultSecurityAlertPolicy());
// Test transparent data encryption settings.
TransparentDataEncryption transparentDataEncryption = sqlDatabase.getTransparentDataEncryption();
@@ -1024,6 +1076,13 @@ public void canManageReplicationLinks() throws Exception {
.define(SQL_DATABASE_NAME)
.withSourceDatabase(databaseInServer1.id())
.withMode(CreateMode.ONLINE_SECONDARY)
+ // Explicitly pin the secondary to Standard S0 (DTU-based) to match the primary.
+ // Without an explicit edition, Azure SQL applies the region's default tier —currently
+ // GeneralPurpose / GP_S_Gen5 (serverless, vCore-based), which counts against the
+ // subscription's vCore quota and can fail with
+ // "subscription would exceed the allowed vCore quota of 500" when the quota is near
+ // its limit (e.g. due to leftover resources from earlier test runs).
+ .withStandardEdition(SqlDatabaseStandardServiceObjective.S0)
.create();
ResourceManagerUtils.sleep(Duration.ofSeconds(2));
List replicationLinksInDb1
@@ -1050,9 +1109,23 @@ public void canManageReplicationLinks() throws Exception {
replicationLinksInDb1.get(0).forceFailoverAllowDataLoss();
replicationLinksInDb1.get(0).refresh();
- ResourceManagerUtils.sleep(Duration.ofSeconds(30));
+ // Wait until the relationship has entered and then left SUSPENDED after
+ // forceFailoverAllowDataLoss; otherwise delete() fails with
+ // 409 GeoReplicationCannotBecomePrimaryDuringUndo.
+ ReplicationLink secondaryLink = replicationLinksInDb2.get(0);
+ int maxAttempts = 300;
+ boolean sawSuspended = false;
+ while (maxAttempts-- > 0) {
+ secondaryLink.refresh();
+ if (secondaryLink.replicationState() == ReplicationState.SUSPENDED) {
+ sawSuspended = true;
+ } else if (sawSuspended) {
+ break;
+ }
+ ResourceManagerUtils.sleep(Duration.ofSeconds(10));
+ }
- replicationLinksInDb2.get(0).delete();
+ secondaryLink.delete();
Assertions.assertEquals(databaseInServer2.listReplicationLinks().size(), 0);
sqlServer1.databases().delete(databaseInServer1.name());
@@ -1077,7 +1150,12 @@ public void canDoOperationsOnDataWarehouse() throws Exception {
Mono resourceStream = sqlServer.databases()
.define(SQL_DATABASE_NAME)
.withCollation(COLLATION)
- .withSku(DatabaseSku.DATAWAREHOUSE_DW1000C)
+ // Use DW100c (the smallest DataWarehouse SKU) instead of DW1000c.
+ // DW1000c provisions ~5 compute nodes and consumes a large share of the subscription's
+ // vCore quota (500). The test only exercises pause/resume/list operations, none of
+ // which require a large warehouse, so DW100c is sufficient and avoids the
+ // "subscription would exceed the allowed vCore quota of 500" failure.
+ .withSku(DatabaseSku.DATAWAREHOUSE_DW100C)
.createAsync();
SqlDatabase sqlDatabase = resourceStream.block();
@@ -1447,12 +1525,13 @@ private SqlServer createSqlServer() {
}
private SqlServer createSqlServer(String sqlServerName) {
+ AzureUser user = azureCliSignedInUser();
return sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin("userName")
- .withAdministratorPassword("P@ssword~1")
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.create();
}
@@ -1474,7 +1553,7 @@ private void validateSqlFirewallRule(SqlFirewallRule sqlFirewallRule, String fir
Assertions.assertEquals(END_IPADDRESS, sqlFirewallRule.endIpAddress());
Assertions.assertEquals(rgName, sqlFirewallRule.resourceGroupName());
Assertions.assertEquals(sqlServerName, sqlFirewallRule.sqlServerName());
- Assertions.assertEquals(Region.US_EAST, sqlFirewallRule.region());
+ Assertions.assertEquals(DEFAULT_REGION, sqlFirewallRule.region());
}
private static void validateListSqlElasticPool(List sqlElasticPools) {
@@ -1517,7 +1596,6 @@ private void validateSqlServer(SqlServer sqlServer) {
Assertions.assertEquals(rgName, sqlServer.resourceGroupName());
Assertions.assertNotNull(sqlServer.fullyQualifiedDomainName());
// Assertions.assertEquals(ServerVersion.ONE_TWO_FULL_STOP_ZERO, sqlServer.version());
- Assertions.assertEquals("userName", sqlServer.administratorLogin());
}
private void validateSqlDatabase(SqlDatabase sqlDatabase, String databaseName) {
@@ -1537,46 +1615,48 @@ private void validateSqlDatabaseWithElasticPool(SqlDatabase sqlDatabase, String
@DoNotRecord(skipInPlayback = true)
public void testRandomSku() {
// LiveOnly because "test timing out after latest test proxy update"
- // "M" series is not supported in this region
+ // "M" series is not supported in this region; "Fsv2" series has been deprecated
+ List excludedFamilies = Arrays.asList("M", "Fsv2");
List capabilityStatusList
= Arrays.asList(CapabilityStatus.AVAILABLE, CapabilityStatus.DEFAULT);
List databaseSkus = DatabaseSku.getAll()
.stream()
- .filter(sku -> !"M".equals(sku.toSku().family()))
+ .filter(sku -> !excludedFamilies.contains(sku.toSku().family()))
.collect(Collectors.toCollection(LinkedList::new));
Collections.shuffle(databaseSkus);
List elasticPoolSkus = ElasticPoolSku.getAll()
.stream()
- .filter(sku -> !"M".equals(sku.toSku().family()))
+ .filter(sku -> !excludedFamilies.contains(sku.toSku().family()))
.collect(Collectors.toCollection(LinkedList::new));
Collections.shuffle(elasticPoolSkus);
sqlServerManager.sqlServers()
- .getCapabilitiesByRegion(Region.US_EAST)
+ .getCapabilitiesByRegion(DEFAULT_REGION)
.supportedCapabilitiesByServerVersion()
.forEach((x, serverVersionCapability) -> {
serverVersionCapability.supportedEditions().forEach(edition -> {
edition.supportedServiceLevelObjectives()
.stream()
.filter(serviceObjective -> !capabilityStatusList.contains(serviceObjective.status())
- || "M".equals(serviceObjective.sku().family()))
+ || excludedFamilies.contains(serviceObjective.sku().family()))
.forEach(serviceObjective -> databaseSkus.remove(DatabaseSku.fromSku(serviceObjective.sku())));
});
serverVersionCapability.supportedElasticPoolEditions().forEach(edition -> {
edition.supportedElasticPoolPerformanceLevels()
.stream()
.filter(performance -> !capabilityStatusList.contains(performance.status())
- || "M".equals(performance.sku().family()))
+ || excludedFamilies.contains(performance.sku().family()))
.forEach(performance -> elasticPoolSkus.remove(ElasticPoolSku.fromSku(performance.sku())));
});
});
+ AzureUser user = azureCliSignedInUser();
SqlServer sqlServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin("userName")
- .withAdministratorPassword(password())
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.create();
// Too many elastic pools defined will hit sql server DTU quota limits.
@@ -1603,7 +1683,7 @@ public void generateSku() throws IOException {
StringBuilder databaseSkuBuilder = new StringBuilder();
StringBuilder elasticPoolSkuBuilder = new StringBuilder();
sqlServerManager.sqlServers()
- .getCapabilitiesByRegion(Region.US_EAST)
+ .getCapabilitiesByRegion(DEFAULT_REGION)
.supportedCapabilitiesByServerVersion()
.forEach((x, serverVersionCapability) -> {
serverVersionCapability.supportedEditions().forEach(edition -> {
@@ -1640,7 +1720,7 @@ public void generateSku() throws IOException {
Files.write(new File("src/main/java/com/azure/resourcemanager/sql/models/ElasticPoolSku.java").toPath(),
elasticPoolSku.getBytes(StandardCharsets.UTF_8));
- sqlServerManager.resourceManager().resourceGroups().define(rgName).withRegion(Region.US_EAST).create(); // for deletion
+ sqlServerManager.resourceManager().resourceGroups().define(rgName).withRegion(DEFAULT_REGION).create(); // for deletion
}
private byte[] readAllBytes(InputStream inputStream) throws IOException {
@@ -1688,13 +1768,14 @@ private void addStaticSkuDefinition(StringBuilder builder, String edition, Strin
@Test
public void canCreateAndUpdatePublicNetworkAccess() {
+ AzureUser user = azureCliSignedInUser();
// Create
SqlServer sqlServer = sqlServerManager.sqlServers()
.define(sqlServerName)
- .withRegion(Region.US_EAST)
+ .withRegion(DEFAULT_REGION)
.withNewResourceGroup(rgName)
- .withAdministratorLogin("userName")
- .withAdministratorPassword("P@ssword~1")
+ .withAzureActiveDirectoryOnlyAuthentication()
+ .withExternalActiveDirectoryAdministrator(user.userPrincipalName(), user.id(), PrincipalType.USER)
.withoutAccessFromAzureServices()
.disablePublicNetworkAccess()
.create();
diff --git a/sdk/sql/azure-resourcemanager-sql/src/test/java/com/azure/resourcemanager/sql/SqlServerTest.java b/sdk/sql/azure-resourcemanager-sql/src/test/java/com/azure/resourcemanager/sql/SqlServerTest.java
index d0e69fa5a50b..96df532f8dc3 100644
--- a/sdk/sql/azure-resourcemanager-sql/src/test/java/com/azure/resourcemanager/sql/SqlServerTest.java
+++ b/sdk/sql/azure-resourcemanager-sql/src/test/java/com/azure/resourcemanager/sql/SqlServerTest.java
@@ -10,6 +10,8 @@
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.RetryPolicy;
import com.azure.core.management.profile.AzureProfile;
+import com.azure.resourcemanager.authorization.AuthorizationManager;
+import com.azure.resourcemanager.msi.MsiManager;
import com.azure.resourcemanager.resources.fluentcore.utils.HttpPipelineProvider;
import com.azure.resourcemanager.resources.fluentcore.utils.ResourceManagerUtils;
import com.azure.resourcemanager.resources.ResourceManager;
@@ -26,6 +28,8 @@ public abstract class SqlServerTest extends ResourceManagerTestProxyTestBase {
protected ResourceManager resourceManager;
protected SqlServerManager sqlServerManager;
protected StorageManager storageManager;
+ protected MsiManager msiManager;
+ protected AuthorizationManager authorizationManager;
protected String rgName = "";
protected String sqlServerName = "";
@@ -45,6 +49,8 @@ protected void initializeClients(HttpPipeline httpPipeline, AzureProfile profile
internalContext.setIdentifierFunction(name -> new TestIdentifierProvider(testResourceNamer));
sqlServerManager = buildManager(SqlServerManager.class, httpPipeline, profile);
storageManager = buildManager(StorageManager.class, httpPipeline, profile);
+ msiManager = buildManager(MsiManager.class, httpPipeline, profile);
+ authorizationManager = buildManager(AuthorizationManager.class, httpPipeline, profile);
resourceManager = sqlServerManager.resourceManager();
setInternalContext(internalContext, sqlServerManager);
}