From 6e0b4e6357efdbe7896f37f8d65539e67444aa0b Mon Sep 17 00:00:00 2001 From: strehan1993 <70013163+strehan1993@users.noreply.github.com> Date: Tue, 1 Jun 2021 01:02:19 -0700 Subject: [PATCH] Code changes for provisioning Server & ManagedInstance with AD Only Authentication & External Administrator Properties (#15151) * Rebase master * Update Changelog * Switch to singular noun * Fix bug * Check for administrator object * Fix test --- .../AzureSqlDatabaseServerAttributeTests.cs | 2 +- src/Sql/Sql/ChangeLog.md | 3 + .../Cmdlet/GetAzureSqlManagedInstance.cs | 17 +- .../Cmdlet/NewAzureSqlManagedInstance.cs | 47 +++- .../Model/AzureSqlManagedInstanceModel.cs | 5 + .../AzureSqlManagedInstanceAdapter.cs | 250 +++++++++++++++++- .../AzureSqlManagedInstanceCommunicator.cs | 16 +- src/Sql/Sql/Properties/Resources.Designer.cs | 20 +- src/Sql/Sql/Properties/Resources.resx | 10 +- .../Sql/Server/Cmdlet/GetAzureSqlServer.cs | 17 +- .../Sql/Server/Cmdlet/NewAzureSqlServer.cs | 47 +++- .../Sql/Server/Model/AzureSqlServerModel.cs | 6 + .../Server/Services/AzureSqlServerAdapter.cs | 244 ++++++++++++++++- .../Services/AzureSqlServerCommunicator.cs | 12 +- src/Sql/Sql/help/Get-AzSqlInstance.md | 116 +++++++- src/Sql/Sql/help/Get-AzSqlServer.md | 85 +++++- src/Sql/Sql/help/New-AzSqlInstance.md | 116 +++++++- src/Sql/Sql/help/New-AzSqlServer.md | 85 +++++- 18 files changed, 1025 insertions(+), 73 deletions(-) diff --git a/src/Sql/Sql.Test/UnitTests/AzureSqlDatabaseServerAttributeTests.cs b/src/Sql/Sql.Test/UnitTests/AzureSqlDatabaseServerAttributeTests.cs index caa55ddad1ab..efd7d0f1333f 100644 --- a/src/Sql/Sql.Test/UnitTests/AzureSqlDatabaseServerAttributeTests.cs +++ b/src/Sql/Sql.Test/UnitTests/AzureSqlDatabaseServerAttributeTests.cs @@ -38,7 +38,7 @@ public void NewAzureSqlServerAttributes() UnitTestHelper.CheckConfirmImpact(type, System.Management.Automation.ConfirmImpact.Low); UnitTestHelper.CheckCmdletParameterAttributes(type, "ServerName", isMandatory: true, valueFromPipelineByName: false); - UnitTestHelper.CheckCmdletParameterAttributes(type, "SqlAdministratorCredentials", isMandatory: true, valueFromPipelineByName: false); + UnitTestHelper.CheckCmdletParameterAttributes(type, "SqlAdministratorCredentials", isMandatory: false, valueFromPipelineByName: false); UnitTestHelper.CheckCmdletParameterAttributes(type, "Tags", isMandatory: false, valueFromPipelineByName: false); UnitTestHelper.CheckCmdletParameterAttributes(type, "ServerVersion", isMandatory: false, valueFromPipelineByName: false); } diff --git a/src/Sql/Sql/ChangeLog.md b/src/Sql/Sql/ChangeLog.md index c9cd17cdc74e..f3ee6a45fa04 100644 --- a/src/Sql/Sql/ChangeLog.md +++ b/src/Sql/Sql/ChangeLog.md @@ -20,6 +20,9 @@ ## Upcoming Release * Added option to support short version of maintenance configuration id for Managed Instance in `New-AzSqlInstance` and `Set-AzSqlInstance` cmdlets * Added HighAvailabilityReplicaCount to `New-AzSqlDatabaseSecondary` +* Add External Administrator and AAD Only Properties to AzSqlServer and AzSqlInstance + - Added option to specify `-ExternalAdminName`, `-ExternalAdminSid`, `-EnableActiveDirectoryOnlyAuthentication` in `New-AzSqlInstance` and `Set-AzSqlInstance` cmdlets + - Added option to expand external administrators information using `-ExpandActiveDirectoryAdministrator` in `Get-AzSqlServer` and `Get-AzSqlInstance` cmdlets ## Version 3.1.0 * Updated `Set-AzSqlDatabaseVulnerabilityAssessmentRuleBaseline` documentation to include example of define array of array with one inner array. diff --git a/src/Sql/Sql/ManagedInstance/Cmdlet/GetAzureSqlManagedInstance.cs b/src/Sql/Sql/ManagedInstance/Cmdlet/GetAzureSqlManagedInstance.cs index 9b26cf1a7aaa..8dcb0f6f517a 100644 --- a/src/Sql/Sql/ManagedInstance/Cmdlet/GetAzureSqlManagedInstance.cs +++ b/src/Sql/Sql/ManagedInstance/Cmdlet/GetAzureSqlManagedInstance.cs @@ -112,6 +112,13 @@ public class GetAzureSqlManagedInstance : ManagedInstanceCmdletBase [ValidateNotNullOrEmpty] public override string ResourceGroupName { get; set; } + /// + /// Expand Active Directory Administrator Information on the Managed Instance + /// + [Parameter(Mandatory = false, + HelpMessage = "Expand Active Directory Administrator Information on the server.")] + public SwitchParameter ExpandActiveDirectoryAdministrator { get; set; } + /// /// Entry point for the cmdlet /// @@ -148,25 +155,27 @@ protected override IEnumerable GetEntity() { ICollection results = new List(); + string expand = (this.ExpandActiveDirectoryAdministrator.IsPresent) ? "administrators/activeDirectory" : null; + if (ShouldGetByName(ResourceGroupName, Name)) { results = new List(); - results.Add(ModelAdapter.GetManagedInstance(this.ResourceGroupName, this.Name)); + results.Add(ModelAdapter.GetManagedInstance(this.ResourceGroupName, this.Name, expand)); } else if (ShouldListByResourceGroup(ResourceGroupName, Name)) { if (this.InstancePoolName != null) { - results = ModelAdapter.ListManagedInstancesByInstancePool(this.ResourceGroupName, this.InstancePoolName); + results = ModelAdapter.ListManagedInstancesByInstancePool(this.ResourceGroupName, this.InstancePoolName, expand); } else { - results = ModelAdapter.ListManagedInstancesByResourceGroup(this.ResourceGroupName); + results = ModelAdapter.ListManagedInstancesByResourceGroup(this.ResourceGroupName, expand); } } else { - results = ModelAdapter.ListManagedInstances(); + results = ModelAdapter.ListManagedInstances(expand); } return TopLevelWildcardFilter(ResourceGroupName, Name, results); diff --git a/src/Sql/Sql/ManagedInstance/Cmdlet/NewAzureSqlManagedInstance.cs b/src/Sql/Sql/ManagedInstance/Cmdlet/NewAzureSqlManagedInstance.cs index 08c3101758c7..2a12d8951535 100644 --- a/src/Sql/Sql/ManagedInstance/Cmdlet/NewAzureSqlManagedInstance.cs +++ b/src/Sql/Sql/ManagedInstance/Cmdlet/NewAzureSqlManagedInstance.cs @@ -28,6 +28,7 @@ using Microsoft.WindowsAzure.Commands.Utilities.Common; using Microsoft.Azure.Commands.Sql.Instance_Pools.Services; using Microsoft.Azure.Management.Internal.Resources.Utilities.Models; +using System; namespace Microsoft.Azure.Commands.Sql.ManagedInstance.Cmdlet { @@ -120,8 +121,7 @@ public class NewAzureSqlManagedInstance : ManagedInstanceCmdletBase /// /// Gets or sets the admin credential of the instance /// - [Parameter(Mandatory = true, HelpMessage = "The SQL authentication credential of the instance.")] - [ValidateNotNull] + [Parameter(Mandatory = false, HelpMessage = "The SQL authentication credential of the instance.")] public PSCredential AdministratorCredential { get; set; } /// @@ -335,11 +335,42 @@ public class NewAzureSqlManagedInstance : ManagedInstanceCmdletBase [Parameter(HelpMessage = "Skip confirmation message for performing the action")] public SwitchParameter Force { get; set; } + /// + /// Enable Active Directory Only Authentication on the server + /// + [Parameter(Mandatory = false, + HelpMessage = "Enable Active Directory Only Authentication on the server.")] + public SwitchParameter EnableActiveDirectoryOnlyAuthentication { get; set; } + + /// + /// Azure Active Directory display name for a user or group + /// + [Parameter(Mandatory = false, + HelpMessage = "Specifies the display name of the user, group or application which is the Azure Active Directory administrator for the server. This display name must exist in the active directory associated with the current subscription.")] + public string ExternalAdminName { get; set; } + + /// + /// Azure Active Directory object id for a user, group or application + /// + [Parameter(Mandatory = false, + HelpMessage = "Specifies the object ID of the user, group or application which is the Azure Active Directory administrator.")] + public Guid? ExternalAdminSID { get; set; } + /// /// Overriding to add warning message /// public override void ExecuteCmdlet() { + if (this.EnableActiveDirectoryOnlyAuthentication.IsPresent && this.ExternalAdminName == null) + { + throw new PSArgumentException(Properties.Resources.MissingExternalAdmin, "ExternalAdminName"); + } + + if (!this.EnableActiveDirectoryOnlyAuthentication.IsPresent && this.AdministratorCredential == null) + { + throw new PSArgumentException(Properties.Resources.MissingSQLAdministratorCredentials, "AdministratorCredential"); + } + if (this.IsParameterBound(c => c.InstancePool)) { this.ResourceGroupName = this.InstancePool.ResourceGroupName; @@ -462,8 +493,8 @@ public override void ExecuteCmdlet() Location = this.Location, ResourceGroupName = this.ResourceGroupName, FullyQualifiedDomainName = this.Name, - AdministratorLogin = this.AdministratorCredential.UserName, - AdministratorPassword = this.AdministratorCredential.Password, + AdministratorPassword = (this.AdministratorCredential != null) ? this.AdministratorCredential.Password : null, + AdministratorLogin = (this.AdministratorCredential != null) ? this.AdministratorCredential.UserName : null, Tags = TagsConversionHelper.CreateTagDictionary(Tag, validate: true), Identity = ResourceIdentityHelper.GetIdentityObjectFromType(this.AssignIdentity.IsPresent), LicenseType = this.LicenseType, @@ -483,7 +514,13 @@ public override void ExecuteCmdlet() InstancePoolName = this.InstancePoolName, MinimalTlsVersion = this.MinimalTlsVersion, BackupStorageRedundancy = this.BackupStorageRedundancy, - MaintenanceConfigurationId = this.MaintenanceConfigurationId + MaintenanceConfigurationId = this.MaintenanceConfigurationId, + Administrators = new Management.Sql.Models.ManagedInstanceExternalAdministrator() + { + AzureADOnlyAuthentication = (this.EnableActiveDirectoryOnlyAuthentication.IsPresent) ? (bool?)true : null, + Login = this.ExternalAdminName, + Sid = this.ExternalAdminSID + } }); return newEntity; } diff --git a/src/Sql/Sql/ManagedInstance/Model/AzureSqlManagedInstanceModel.cs b/src/Sql/Sql/ManagedInstance/Model/AzureSqlManagedInstanceModel.cs index b031a7d9d702..ed4fbeadc8dc 100644 --- a/src/Sql/Sql/ManagedInstance/Model/AzureSqlManagedInstanceModel.cs +++ b/src/Sql/Sql/ManagedInstance/Model/AzureSqlManagedInstanceModel.cs @@ -144,5 +144,10 @@ public class AzureSqlManagedInstanceModel /// Gets or sets the managed instance maintenance configuration id /// public string MaintenanceConfigurationId { get; set; } + + /// + /// Gets or sets the Azure SQL Managed Instance Active Directory administrator + /// + public Management.Sql.Models.ManagedInstanceExternalAdministrator Administrators { get; set; } } } diff --git a/src/Sql/Sql/ManagedInstance/Services/AzureSqlManagedInstanceAdapter.cs b/src/Sql/Sql/ManagedInstance/Services/AzureSqlManagedInstanceAdapter.cs index 9462ea72541f..c89f9c2bdab0 100644 --- a/src/Sql/Sql/ManagedInstance/Services/AzureSqlManagedInstanceAdapter.cs +++ b/src/Sql/Sql/ManagedInstance/Services/AzureSqlManagedInstanceAdapter.cs @@ -25,6 +25,10 @@ using Microsoft.Azure.Commands.Sql.Common; using Microsoft.WindowsAzure.Commands.Common; using Microsoft.Azure.Management.Internal.Resources.Utilities.Models; +using Microsoft.Azure.Management.Sql.Models; +using Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory; +using Microsoft.Azure.Graph.RBAC.Version1_6.Models; +using Microsoft.Rest.Azure.OData; namespace Microsoft.Azure.Commands.Sql.ManagedInstance.Adapter { @@ -43,6 +47,33 @@ public class AzureSqlManagedInstanceAdapter /// public IAzureContext Context { get; set; } + /// + /// A private instance of ActiveDirectoryClient + /// + private ActiveDirectoryClient _activeDirectoryClient; + + /// + /// Gets or sets the Azure ActiveDirectoryClient instance + /// + public ActiveDirectoryClient ActiveDirectoryClient + { + get + { + if (_activeDirectoryClient == null) + { + _activeDirectoryClient = new ActiveDirectoryClient(Context); + if (!Context.Environment.IsEndpointSet(AzureEnvironment.Endpoint.Graph)) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.InvalidGraphEndpoint)); + } + _activeDirectoryClient = new ActiveDirectoryClient(Context); + } + return this._activeDirectoryClient; + } + + set { this._activeDirectoryClient = value; } + } + /// /// Constructs a Managed instance adapter /// @@ -59,9 +90,9 @@ public AzureSqlManagedInstanceAdapter(IAzureContext context) /// The name of the resource group /// The name of the managed instance /// The managed instance - public AzureSqlManagedInstanceModel GetManagedInstance(string resourceGroupName, string managedInstanceName) + public AzureSqlManagedInstanceModel GetManagedInstance(string resourceGroupName, string managedInstanceName, string expand = null) { - var resp = Communicator.Get(resourceGroupName, managedInstanceName); + var resp = Communicator.Get(resourceGroupName, managedInstanceName, expand); return CreateManagedInstanceModelFromResponse(resp); } @@ -69,9 +100,9 @@ public AzureSqlManagedInstanceModel GetManagedInstance(string resourceGroupName, /// Gets a list of all the managed instances in a subscription /// /// A list of all the managed instances - public List ListManagedInstances() + public List ListManagedInstances(string expand = null) { - var resp = Communicator.List(); + var resp = Communicator.List(expand); return resp.Select((s) => CreateManagedInstanceModelFromResponse(s)).ToList(); } @@ -80,9 +111,9 @@ public List ListManagedInstances() /// Gets a list of all managed instances in an instance pool /// /// A list of all managed instances in an instance pool - public List ListManagedInstancesByInstancePool(string resourceGroupName, string instancePoolName) + public List ListManagedInstancesByInstancePool(string resourceGroupName, string instancePoolName, string expand = null) { - var resp = Communicator.ListByInstancePool(resourceGroupName, instancePoolName); + var resp = Communicator.ListByInstancePool(resourceGroupName, instancePoolName, expand); return resp.Select((s) => CreateManagedInstanceModelFromResponse(s)).ToList(); } @@ -91,9 +122,9 @@ public List ListManagedInstancesByInstancePool(str /// /// The name of the resource group /// A list of all the managed instances - public List ListManagedInstancesByResourceGroup(string resourceGroupName) + public List ListManagedInstancesByResourceGroup(string resourceGroupName, string expand = null) { - var resp = Communicator.ListByResourceGroup(resourceGroupName); + var resp = Communicator.ListByResourceGroup(resourceGroupName, expand); return resp.Select((s) => { return CreateManagedInstanceModelFromResponse(s); @@ -141,6 +172,7 @@ public AzureSqlManagedInstanceModel UpsertManagedInstance(AzureSqlManagedInstanc MinimalTlsVersion = model.MinimalTlsVersion, StorageAccountType = MapExternalBackupStorageRedundancyToInternal(model.BackupStorageRedundancy), MaintenanceConfigurationId = MaintenanceConfigurationHelper.ConvertMaintenanceConfigurationIdArgument(model.MaintenanceConfigurationId, Context.Subscription.Id), + Administrators = GetActiveDirectoryInformation(model.Administrators) }); return CreateManagedInstanceModelFromResponse(resp); @@ -164,8 +196,8 @@ public AzureSqlManagedInstanceModel UpdateManagedInstance(AzureSqlManagedInstanc SubnetId = model.SubnetId, VCores = model.VCores, PublicDataEndpointEnabled = model.PublicDataEndpointEnabled, - ProxyOverride = model.ProxyOverride - }); + ProxyOverride = model.ProxyOverride, + }); return CreateManagedInstanceModelFromResponse(resp); } @@ -225,6 +257,12 @@ private static AzureSqlManagedInstanceModel CreateManagedInstanceModelFromRespon sku.Family = resp.Sku.Family; managedInstance.Sku = sku; + managedInstance.Administrators = resp.Administrators; + + if (managedInstance.Administrators != null && managedInstance.Administrators.AdministratorType == null) + { + managedInstance.Administrators.AdministratorType = "ActiveDirectory"; + } return managedInstance; } @@ -291,5 +329,197 @@ public static string MapInternalBackupStorageRedundancyToExternal(string backupS return null; } } + + /// + /// Verifies that the Azure Active Directory user or group exists, and will get the object id if it is not set. + /// + /// Azure Active Directory user or group display name + /// Azure Active Directory user or group object id + /// + protected ManagedInstanceExternalAdministrator GetActiveDirectoryInformation(ManagedInstanceExternalAdministrator input) + { + if (input == null || string.IsNullOrEmpty(input.Login)) + { + return null; + } + + Guid? objectId = input.Sid; + string displayName = input.Login; + bool? adOnlyAuth = input.AzureADOnlyAuthentication; + + // Gets the default Tenant id for the subscriptions + Guid tenantId = GetTenantId(); + + // Check for a Azure Active Directory group. Recommended to always use group. + IEnumerable groupList = null; + PSADGroup group = null; + + var filter = new ADObjectFilterOptions() + { + Id = (objectId != null && objectId != Guid.Empty) ? objectId.ToString() : null, + SearchString = displayName, + Paging = true, + }; + + // Get a list of groups from Azure Active Directory + groupList = ActiveDirectoryClient.FilterGroups(filter).Where(gr => string.Equals(gr.DisplayName, displayName, StringComparison.OrdinalIgnoreCase)); + + if (groupList != null && groupList.Count() > 1) + { + // More than one group was found with that display name. + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADGroupMoreThanOneFound, displayName)); + } + else if (groupList != null && groupList.Count() == 1) + { + // Only one group was found. Get the group display name and object id + group = groupList.First(); + + // Only support Security Groups + if (group.SecurityEnabled.HasValue && !group.SecurityEnabled.Value) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.InvalidADGroupNotSecurity, displayName)); + } + } + + // Lookup for serviceprincipals + ODataQuery odataQueryFilter; + + if ((objectId != null && objectId != Guid.Empty)) + { + var applicationIdString = objectId.ToString(); + odataQueryFilter = new Rest.Azure.OData.ODataQuery(a => a.AppId == applicationIdString); + } + else + { + odataQueryFilter = new Rest.Azure.OData.ODataQuery(a => a.DisplayName == displayName); + } + + var servicePrincipalList = ActiveDirectoryClient.FilterServicePrincipals(odataQueryFilter); + + if (servicePrincipalList != null && servicePrincipalList.Count() > 1) + { + // More than one service principal was found. + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADApplicationMoreThanOneFound, displayName)); + } + else if (servicePrincipalList != null && servicePrincipalList.Count() == 1) + { + // Only one user was found. Get the user display name and object id + PSADServicePrincipal app = servicePrincipalList.First(); + + if (displayName != null && string.CompareOrdinal(displayName, app.DisplayName) != 0) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADApplicationDisplayNameMismatch, displayName, app.DisplayName)); + } + + if (group != null) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADDuplicateGroupAndApplicationFound, displayName)); + } + + return new ManagedInstanceExternalAdministrator() + { + Login = displayName, + Sid = app.ApplicationId, + TenantId = tenantId, + PrincipalType = "Application", + AzureADOnlyAuthentication = adOnlyAuth + }; + } + + if (group != null) + { + return new ManagedInstanceExternalAdministrator() + { + Login = group.DisplayName, + Sid = group.Id, + TenantId = tenantId, + PrincipalType = "Group", + AzureADOnlyAuthentication = adOnlyAuth + }; + } + + // No group or service principal was found. Check for a user + filter = new ADObjectFilterOptions() + { + Id = (objectId != null && objectId != Guid.Empty) ? objectId.ToString() : null, + SearchString = displayName, + Paging = true, + }; + + // Get a list of user from Azure Active Directory + var userList = ActiveDirectoryClient.FilterUsers(filter).Where(gr => string.Equals(gr.DisplayName, displayName, StringComparison.OrdinalIgnoreCase)); + + // No user was found. Check if the display name is a UPN + if (userList == null || userList.Count() == 0) + { + // Check if the display name is the UPN + filter = new ADObjectFilterOptions() + { + Id = (objectId != null && objectId != Guid.Empty) ? objectId.ToString() : null, + UPN = displayName, + Paging = true, + }; + + userList = ActiveDirectoryClient.FilterUsers(filter).Where(gr => string.Equals(gr.UserPrincipalName, displayName, StringComparison.OrdinalIgnoreCase)); + } + + // No user was found. Check if the display name is a guest user. + if (userList == null || userList.Count() == 0) + { + // Check if the display name is the UPN + filter = new ADObjectFilterOptions() + { + Id = (objectId != null && objectId != Guid.Empty) ? objectId.ToString() : null, + Mail = displayName, + Paging = true, + }; + + userList = ActiveDirectoryClient.FilterUsers(filter); + } + + // No user was found + if (userList == null || userList.Count() == 0) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADObjectNotFound, displayName)); + } + else if (userList.Count() > 1) + { + // More than one user was found. + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADUserMoreThanOneFound, displayName)); + } + else + { + // Only one user was found. Get the user display name and object id + var obj = userList.First(); + + return new ManagedInstanceExternalAdministrator() + { + Login = displayName, + Sid = obj.Id, + TenantId = tenantId, + PrincipalType = "User", + AzureADOnlyAuthentication = adOnlyAuth + }; + } + } + + /// + /// Get the default tenantId for the current subscription + /// + /// + protected Guid GetTenantId() + { + var tenantIdStr = Context.Tenant.Id.ToString(); + string adTenant = Context.Environment.GetEndpoint(AzureEnvironment.Endpoint.AdTenant); + string graph = Context.Environment.GetEndpoint(AzureEnvironment.Endpoint.Graph); + var tenantIdGuid = Guid.Empty; + + if (string.IsNullOrWhiteSpace(tenantIdStr) || !Guid.TryParse(tenantIdStr, out tenantIdGuid)) + { + throw new InvalidOperationException(Microsoft.Azure.Commands.Sql.Properties.Resources.InvalidTenantId); + } + + return tenantIdGuid; + } } } diff --git a/src/Sql/Sql/ManagedInstance/Services/AzureSqlManagedInstanceCommunicator.cs b/src/Sql/Sql/ManagedInstance/Services/AzureSqlManagedInstanceCommunicator.cs index 8ef5d56bba2b..367fabb7e1d0 100644 --- a/src/Sql/Sql/ManagedInstance/Services/AzureSqlManagedInstanceCommunicator.cs +++ b/src/Sql/Sql/ManagedInstance/Services/AzureSqlManagedInstanceCommunicator.cs @@ -63,33 +63,33 @@ public AzureSqlManagedInstanceCommunicator(IAzureContext context) /// /// Gets the Managed instance /// - public Management.Sql.Models.ManagedInstance Get(string resourceGroupName, string managedInstanceName) + public Management.Sql.Models.ManagedInstance Get(string resourceGroupName, string managedInstanceName, string expand = null) { - return GetCurrentSqlClient().ManagedInstances.Get(resourceGroupName, managedInstanceName); + return GetCurrentSqlClient().ManagedInstances.Get(resourceGroupName, managedInstanceName, expand); } /// /// Lists Managed instances in a resource group /// - public IList ListByResourceGroup(string resourceGroupName) + public IList ListByResourceGroup(string resourceGroupName, string expand = null) { - return GetCurrentSqlClient().ManagedInstances.ListByResourceGroup(resourceGroupName).ToList(); + return GetCurrentSqlClient().ManagedInstances.ListByResourceGroup(resourceGroupName, expand).ToList(); } /// /// Lists managed instances in an instance pool /// - public IList ListByInstancePool(string resourceGroupName, string instancePoolName) + public IList ListByInstancePool(string resourceGroupName, string instancePoolName, string expand = null) { - return GetCurrentSqlClient().ManagedInstances.ListByInstancePool(resourceGroupName, instancePoolName).ToList(); + return GetCurrentSqlClient().ManagedInstances.ListByInstancePool(resourceGroupName, instancePoolName, expand).ToList(); } /// /// Lists Managed instances /// - public IList List() + public IList List(string expand = null) { - return GetCurrentSqlClient().ManagedInstances.List().ToList(); + return GetCurrentSqlClient().ManagedInstances.List(expand).ToList(); } /// diff --git a/src/Sql/Sql/Properties/Resources.Designer.cs b/src/Sql/Sql/Properties/Resources.Designer.cs index 3809ef8217de..6a27109e2b9e 100644 --- a/src/Sql/Sql/Properties/Resources.Designer.cs +++ b/src/Sql/Sql/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.Azure.Commands.Sql.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -719,6 +719,24 @@ internal static string ManagedInstanceAdvancedDataSecurityIsNotDefined { } } + /// + /// Looks up a localized string similar to ExternalAdminName must be provided when Azure Active Directory Only Authentication is enabled via -EnableActiveDirectoryOnlyAuthentication.. + /// + internal static string MissingExternalAdmin { + get { + return ResourceManager.GetString("MissingExternalAdmin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SQL Administrator Credentials are only optional when Azure Active Directory Only Authentication is enabled via -EnableActiveDirectoryOnlyAuthentication.. + /// + internal static string MissingSQLAdministratorCredentials { + get { + return ResourceManager.GetString("MissingSQLAdministratorCredentials", resourceCulture); + } + } + /// /// Looks up a localized string similar to You need to provide at least one email address or set EmailAdmins to True.. /// diff --git a/src/Sql/Sql/Properties/Resources.resx b/src/Sql/Sql/Properties/Resources.resx index 9d2100c969cd..5b250685316b 100644 --- a/src/Sql/Sql/Properties/Resources.resx +++ b/src/Sql/Sql/Properties/Resources.resx @@ -177,10 +177,10 @@ Please set a valid tenant id in the AzureEnvironment. - + Disables uploading ledger digests - + Enable uploading ledger digests @@ -673,4 +673,10 @@ /subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName>/providers/Microsoft.Sql/locations/<location>/longTermRetentionServers/<serverName>/longTermRetentionDatabases/<databaseName>/longTermRetentionBackups/<backupName> "ResourceId" + + ExternalAdminName must be provided when Azure Active Directory Only Authentication is enabled via -EnableActiveDirectoryOnlyAuthentication. + + + SQL Administrator Credentials are only optional when Azure Active Directory Only Authentication is enabled via -EnableActiveDirectoryOnlyAuthentication. + \ No newline at end of file diff --git a/src/Sql/Sql/Server/Cmdlet/GetAzureSqlServer.cs b/src/Sql/Sql/Server/Cmdlet/GetAzureSqlServer.cs index d1ad62cc6133..f30642d0ebbd 100644 --- a/src/Sql/Sql/Server/Cmdlet/GetAzureSqlServer.cs +++ b/src/Sql/Sql/Server/Cmdlet/GetAzureSqlServer.cs @@ -55,7 +55,14 @@ public class GetAzureSqlServer : AzureSqlServerCmdletBase [ValidateNotNullOrEmpty] [SupportsWildcards] public string ServerName { get; set; } - + + /// + /// Expand Active Directory Administrator Information on the server + /// + [Parameter(Mandatory = false, + HelpMessage = "Expand Active Directory Administrator Information on the server.")] + public SwitchParameter ExpandActiveDirectoryAdministrator { get; set; } + /// /// Gets a server from the service. /// @@ -64,18 +71,20 @@ protected override IEnumerable GetEntity() { ICollection results = null; + string expand = (this.ExpandActiveDirectoryAdministrator.IsPresent) ? "administrators/activedirectory" : null; + if (ShouldGetByName(ResourceGroupName, ServerName)) { results = new List(); - results.Add(ModelAdapter.GetServer(this.ResourceGroupName, this.ServerName)); + results.Add(ModelAdapter.GetServer(this.ResourceGroupName, this.ServerName, expand)); } else if (ShouldListByResourceGroup(ResourceGroupName, ServerName)) { - results = ModelAdapter.ListServersByResourceGroup(this.ResourceGroupName); + results = ModelAdapter.ListServersByResourceGroup(this.ResourceGroupName, expand); } else { - results = ModelAdapter.ListServers(); + results = ModelAdapter.ListServers(expand); } return TopLevelWildcardFilter(ResourceGroupName, ServerName, results); diff --git a/src/Sql/Sql/Server/Cmdlet/NewAzureSqlServer.cs b/src/Sql/Sql/Server/Cmdlet/NewAzureSqlServer.cs index ae04021c06ef..89a952aef4e9 100644 --- a/src/Sql/Sql/Server/Cmdlet/NewAzureSqlServer.cs +++ b/src/Sql/Sql/Server/Cmdlet/NewAzureSqlServer.cs @@ -16,6 +16,7 @@ using Microsoft.Azure.Commands.ResourceManager.Common.Tags; using Microsoft.Azure.Commands.Sql.Common; using Microsoft.Rest.Azure; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -41,9 +42,8 @@ public class NewAzureSqlServer : AzureSqlServerCmdletBase /// /// The SQL administrator credentials for the server /// - [Parameter(Mandatory = true, - HelpMessage = "The SQL administrator credentials for the server")] - [ValidateNotNull] + [Parameter(Mandatory = false, + HelpMessage = "The SQL administrator credentials for the server. Optional when Azure Active Directory only is enabled and an Azure Active Directory administrator is specified.")] public PSCredential SqlAdministratorCredentials { get; set; } /// @@ -95,11 +95,42 @@ public class NewAzureSqlServer : AzureSqlServerCmdletBase [Parameter(Mandatory = false, HelpMessage = "Run cmdlet in the background")] public SwitchParameter AsJob { get; set; } + /// + /// Enable Active Directory Only Authentication on the server + /// + [Parameter(Mandatory = false, + HelpMessage = "Enable Active Directory Only Authentication on the server.")] + public SwitchParameter EnableActiveDirectoryOnlyAuthentication { get; set; } + + /// + /// Azure Active Directory display name for a user or group + /// + [Parameter(Mandatory = false, + HelpMessage = "Specifies the display name of the user, group or application which is the Azure Active Directory administrator for the server. This display name must exist in the active directory associated with the current subscription.")] + public string ExternalAdminName { get; set; } + + /// + /// Azure Active Directory object id for a user, group or application + /// + [Parameter(Mandatory = false, + HelpMessage = "Specifies the object ID of the user, group or application which is the Azure Active Directory administrator.")] + public Guid? ExternalAdminSID { get; set; } + /// /// Overriding to add warning message /// public override void ExecuteCmdlet() { + if (this.EnableActiveDirectoryOnlyAuthentication.IsPresent && this.ExternalAdminName == null) + { + throw new PSArgumentException(Properties.Resources.MissingExternalAdmin, "ExternalAdminName"); + } + + if (!this.EnableActiveDirectoryOnlyAuthentication.IsPresent && this.SqlAdministratorCredentials == null) + { + throw new PSArgumentException(Properties.Resources.MissingSQLAdministratorCredentials, "SqlAdministratorCredentials"); + } + base.ExecuteCmdlet(); } @@ -150,12 +181,18 @@ public override void ExecuteCmdlet() ResourceGroupName = this.ResourceGroupName, ServerName = this.ServerName, ServerVersion = this.ServerVersion, - SqlAdministratorPassword = this.SqlAdministratorCredentials.Password, - SqlAdministratorLogin = this.SqlAdministratorCredentials.UserName, + SqlAdministratorPassword = (this.SqlAdministratorCredentials != null) ? this.SqlAdministratorCredentials.Password : null, + SqlAdministratorLogin = (this.SqlAdministratorCredentials != null) ? this.SqlAdministratorCredentials.UserName : null, Tags = TagsConversionHelper.CreateTagDictionary(Tags, validate: true), Identity = ResourceIdentityHelper.GetIdentityObjectFromType(this.AssignIdentity.IsPresent), MinimalTlsVersion = this.MinimalTlsVersion, PublicNetworkAccess = this.PublicNetworkAccess, + Administrators = new Management.Sql.Models.ServerExternalAdministrator() + { + AzureADOnlyAuthentication = (this.EnableActiveDirectoryOnlyAuthentication.IsPresent) ? (bool?)true : null, + Login = this.ExternalAdminName, + Sid = this.ExternalAdminSID + } }); return newEntity; } diff --git a/src/Sql/Sql/Server/Model/AzureSqlServerModel.cs b/src/Sql/Sql/Server/Model/AzureSqlServerModel.cs index f9ee40a5379f..0e76e82b9dc2 100644 --- a/src/Sql/Sql/Server/Model/AzureSqlServerModel.cs +++ b/src/Sql/Sql/Server/Model/AzureSqlServerModel.cs @@ -13,6 +13,7 @@ // ---------------------------------------------------------------------------------- using Microsoft.Azure.Management.Sql.Models; +using System; using System.Collections.Generic; using System.Security; @@ -81,5 +82,10 @@ public class AzureSqlServerModel /// Gets or sets the flag to control enable/disable public network access /// public string PublicNetworkAccess { get; set; } + + /// + /// Gets or sets the Azure SQL Server Active Directory administrator + /// + public Management.Sql.Models.ServerExternalAdministrator Administrators{ get; set; } } } diff --git a/src/Sql/Sql/Server/Services/AzureSqlServerAdapter.cs b/src/Sql/Sql/Server/Services/AzureSqlServerAdapter.cs index 47fc65ad200c..7e37b75ed875 100644 --- a/src/Sql/Sql/Server/Services/AzureSqlServerAdapter.cs +++ b/src/Sql/Sql/Server/Services/AzureSqlServerAdapter.cs @@ -24,6 +24,10 @@ using System.Security.Permissions; using Microsoft.Azure.Commands.ResourceManager.Common.Tags; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +using Microsoft.Azure.Management.Sql.Models; +using Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory; +using Microsoft.Azure.Graph.RBAC.Version1_6.Models; +using Microsoft.Rest.Azure.OData; namespace Microsoft.Azure.Commands.Sql.Server.Adapter { @@ -42,6 +46,33 @@ public class AzureSqlServerAdapter /// public IAzureContext Context { get; set; } + /// + /// A private instance of ActiveDirectoryClient + /// + private ActiveDirectoryClient _activeDirectoryClient; + + /// + /// Gets or sets the Azure ActiveDirectoryClient instance + /// + public ActiveDirectoryClient ActiveDirectoryClient + { + get + { + if (_activeDirectoryClient == null) + { + _activeDirectoryClient = new ActiveDirectoryClient(Context); + if (!Context.Environment.IsEndpointSet(AzureEnvironment.Endpoint.Graph)) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.InvalidGraphEndpoint)); + } + _activeDirectoryClient = new ActiveDirectoryClient(Context); + } + return this._activeDirectoryClient; + } + + set { this._activeDirectoryClient = value; } + } + /// /// Constructs a server adapter /// @@ -58,9 +89,9 @@ public AzureSqlServerAdapter(IAzureContext context) /// The name of the resource group /// The name of the server /// The server - public AzureSqlServerModel GetServer(string resourceGroupName, string serverName) + public AzureSqlServerModel GetServer(string resourceGroupName, string serverName, string expand = null) { - var resp = Communicator.Get(resourceGroupName, serverName); + var resp = Communicator.Get(resourceGroupName, serverName, expand); return CreateServerModelFromResponse(resp); } @@ -69,9 +100,9 @@ public AzureSqlServerModel GetServer(string resourceGroupName, string serverName /// /// The name of the resource group /// A list of all the servers - public List ListServers() + public List ListServers(string expand = null) { - var resp = Communicator.List(); + var resp = Communicator.List(expand); return resp.Select((s) => { return CreateServerModelFromResponse(s); @@ -83,9 +114,9 @@ public List ListServers() /// /// The name of the resource group /// A list of all the servers - public List ListServersByResourceGroup(string resourceGroupName) + public List ListServersByResourceGroup(string resourceGroupName, string expand = null) { - var resp = Communicator.ListByResourceGroup(resourceGroupName); + var resp = Communicator.ListByResourceGroup(resourceGroupName, expand); return resp.Select((s) => { return CreateServerModelFromResponse(s); @@ -108,7 +139,8 @@ public AzureSqlServerModel UpsertServer(AzureSqlServerModel model) Version = model.ServerVersion, Identity = model.Identity, MinimalTlsVersion = model.MinimalTlsVersion, - PublicNetworkAccess = model.PublicNetworkAccess + PublicNetworkAccess = model.PublicNetworkAccess, + Administrators = GetActiveDirectoryInformation(model.Administrators) }); return CreateServerModelFromResponse(resp); @@ -150,6 +182,12 @@ private static AzureSqlServerModel CreateServerModelFromResponse(Management.Sql. server.ResourceId = resp.Id; server.MinimalTlsVersion = resp.MinimalTlsVersion; server.PublicNetworkAccess = resp.PublicNetworkAccess; + server.Administrators = resp.Administrators; + + if (server.Administrators != null && server.Administrators.AdministratorType == null) + { + server.Administrators.AdministratorType = "ActiveDirectory"; + } return server; } @@ -175,5 +213,197 @@ internal static string Decrypt(SecureString secureString) Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); } } + + /// + /// Verifies that the Azure Active Directory user or group exists, and will get the object id if it is not set. + /// + /// Azure Active Directory user or group display name + /// Azure Active Directory user or group object id + /// + protected ServerExternalAdministrator GetActiveDirectoryInformation(ServerExternalAdministrator input) + { + if (input == null || string.IsNullOrEmpty(input.Login)) + { + return null; + } + + Guid? objectId = input.Sid; + string displayName = input.Login; + bool? adOnlyAuth = input.AzureADOnlyAuthentication; + + // Gets the default Tenant id for the subscriptions + Guid tenantId = GetTenantId(); + + // Check for a Azure Active Directory group. Recommended to always use group. + IEnumerable groupList = null; + PSADGroup group = null; + + var filter = new ADObjectFilterOptions() + { + Id = (objectId != null && objectId != Guid.Empty) ? objectId.ToString() : null, + SearchString = displayName, + Paging = true, + }; + + // Get a list of groups from Azure Active Directory + groupList = ActiveDirectoryClient.FilterGroups(filter).Where(gr => string.Equals(gr.DisplayName, displayName, StringComparison.OrdinalIgnoreCase)); + + if (groupList != null && groupList.Count() > 1) + { + // More than one group was found with that display name. + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADGroupMoreThanOneFound, displayName)); + } + else if (groupList != null && groupList.Count() == 1) + { + // Only one group was found. Get the group display name and object id + group = groupList.First(); + + // Only support Security Groups + if (group.SecurityEnabled.HasValue && !group.SecurityEnabled.Value) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.InvalidADGroupNotSecurity, displayName)); + } + } + + // Lookup for serviceprincipals + ODataQuery odataQueryFilter; + + if ((objectId != null && objectId != Guid.Empty)) + { + var applicationIdString = objectId.ToString(); + odataQueryFilter = new Rest.Azure.OData.ODataQuery(a => a.AppId == applicationIdString); + } + else + { + odataQueryFilter = new Rest.Azure.OData.ODataQuery(a => a.DisplayName == displayName); + } + + var servicePrincipalList = ActiveDirectoryClient.FilterServicePrincipals(odataQueryFilter); + + if (servicePrincipalList != null && servicePrincipalList.Count() > 1) + { + // More than one service principal was found. + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADApplicationMoreThanOneFound, displayName)); + } + else if (servicePrincipalList != null && servicePrincipalList.Count() == 1) + { + // Only one user was found. Get the user display name and object id + PSADServicePrincipal app = servicePrincipalList.First(); + + if (displayName != null && string.CompareOrdinal(displayName, app.DisplayName) != 0) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADApplicationDisplayNameMismatch, displayName, app.DisplayName)); + } + + if (group != null) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADDuplicateGroupAndApplicationFound, displayName)); + } + + return new ServerExternalAdministrator() + { + Login = displayName, + Sid = app.ApplicationId, + TenantId = tenantId, + PrincipalType = "Application", + AzureADOnlyAuthentication = adOnlyAuth + }; + } + + if (group != null) + { + return new ServerExternalAdministrator() + { + Login = group.DisplayName, + Sid = group.Id, + TenantId = tenantId, + PrincipalType = "Group", + AzureADOnlyAuthentication = adOnlyAuth + }; + } + + // No group or service principal was found. Check for a user + filter = new ADObjectFilterOptions() + { + Id = (objectId != null && objectId != Guid.Empty) ? objectId.ToString() : null, + SearchString = displayName, + Paging = true, + }; + + // Get a list of user from Azure Active Directory + var userList = ActiveDirectoryClient.FilterUsers(filter).Where(gr => string.Equals(gr.DisplayName, displayName, StringComparison.OrdinalIgnoreCase)); + + // No user was found. Check if the display name is a UPN + if (userList == null || userList.Count() == 0) + { + // Check if the display name is the UPN + filter = new ADObjectFilterOptions() + { + Id = (objectId != null && objectId != Guid.Empty) ? objectId.ToString() : null, + UPN = displayName, + Paging = true, + }; + + userList = ActiveDirectoryClient.FilterUsers(filter).Where(gr => string.Equals(gr.UserPrincipalName, displayName, StringComparison.OrdinalIgnoreCase)); + } + + // No user was found. Check if the display name is a guest user. + if (userList == null || userList.Count() == 0) + { + // Check if the display name is the UPN + filter = new ADObjectFilterOptions() + { + Id = (objectId != null && objectId != Guid.Empty) ? objectId.ToString() : null, + Mail = displayName, + Paging = true, + }; + + userList = ActiveDirectoryClient.FilterUsers(filter); + } + + // No user was found + if (userList == null || userList.Count() == 0) + { + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADObjectNotFound, displayName)); + } + else if (userList.Count() > 1) + { + // More than one user was found. + throw new ArgumentException(string.Format(Microsoft.Azure.Commands.Sql.Properties.Resources.ADUserMoreThanOneFound, displayName)); + } + else + { + // Only one user was found. Get the user display name and object id + var obj = userList.First(); + + return new ServerExternalAdministrator() + { + Login = displayName, + Sid = obj.Id, + TenantId = tenantId, + PrincipalType = "User", + AzureADOnlyAuthentication = adOnlyAuth + }; + } + } + + /// + /// Get the default tenantId for the current subscription + /// + /// + protected Guid GetTenantId() + { + var tenantIdStr = Context.Tenant.Id.ToString(); + string adTenant = Context.Environment.GetEndpoint(AzureEnvironment.Endpoint.AdTenant); + string graph = Context.Environment.GetEndpoint(AzureEnvironment.Endpoint.Graph); + var tenantIdGuid = Guid.Empty; + + if (string.IsNullOrWhiteSpace(tenantIdStr) || !Guid.TryParse(tenantIdStr, out tenantIdGuid)) + { + throw new InvalidOperationException(Microsoft.Azure.Commands.Sql.Properties.Resources.InvalidTenantId); + } + + return tenantIdGuid; + } } } diff --git a/src/Sql/Sql/Server/Services/AzureSqlServerCommunicator.cs b/src/Sql/Sql/Server/Services/AzureSqlServerCommunicator.cs index 5d211c0eba9f..2768cc9279ad 100644 --- a/src/Sql/Sql/Server/Services/AzureSqlServerCommunicator.cs +++ b/src/Sql/Sql/Server/Services/AzureSqlServerCommunicator.cs @@ -58,25 +58,25 @@ public AzureSqlServerCommunicator(IAzureContext context) /// /// Gets the Azure Sql Database SErver /// - public Management.Sql.Models.Server Get(string resourceGroupName, string serverName) + public Management.Sql.Models.Server Get(string resourceGroupName, string serverName, string expand = null) { - return GetCurrentSqlClient().Servers.Get(resourceGroupName, serverName); + return GetCurrentSqlClient().Servers.Get(resourceGroupName, serverName, expand); } /// /// Lists Azure Sql Servers in a resource group /// - public IList ListByResourceGroup(string resourceGroupName) + public IList ListByResourceGroup(string resourceGroupName, string expand = null) { - return GetCurrentSqlClient().Servers.ListByResourceGroup(resourceGroupName).ToList(); + return GetCurrentSqlClient().Servers.ListByResourceGroup(resourceGroupName, expand).ToList(); } /// /// Lists Azure Sql Servers /// - public IList List() + public IList List(string expand = null) { - return GetCurrentSqlClient().Servers.List().ToList(); + return GetCurrentSqlClient().Servers.List(expand).ToList(); } /// diff --git a/src/Sql/Sql/help/Get-AzSqlInstance.md b/src/Sql/Sql/help/Get-AzSqlInstance.md index d0e49534579b..499b8fc7e0cb 100644 --- a/src/Sql/Sql/help/Get-AzSqlInstance.md +++ b/src/Sql/Sql/help/Get-AzSqlInstance.md @@ -14,31 +14,32 @@ Returns information about Azure SQL Managed Database Instance. ### DefaultParameterSet (Default) ``` -Get-AzSqlInstance [-Name ] [-ResourceGroupName ] [-DefaultProfile ] - [] +Get-AzSqlInstance [-Name ] [-ResourceGroupName ] [-ExpandActiveDirectoryAdministrator] + [-DefaultProfile ] [] ``` ### ListByInstancePoolObjectParameterSet ``` -Get-AzSqlInstance [-InstancePool] [-DefaultProfile ] - [] +Get-AzSqlInstance [-InstancePool] [-ExpandActiveDirectoryAdministrator] + [-DefaultProfile ] [] ``` ### ListByInstancePoolResourceIdentiferParameterSet ``` -Get-AzSqlInstance [-InstancePoolResourceId] [-DefaultProfile ] - [] +Get-AzSqlInstance [-InstancePoolResourceId] [-ExpandActiveDirectoryAdministrator] + [-DefaultProfile ] [] ``` ### GetByManagedInstanceResourceIdentifierParameterSet ``` -Get-AzSqlInstance [-ResourceId] [-DefaultProfile ] [] +Get-AzSqlInstance [-ResourceId] [-ExpandActiveDirectoryAdministrator] + [-DefaultProfile ] [] ``` ### ListByInstancePoolParameterSet ``` Get-AzSqlInstance [-InstancePoolName] -ResourceGroupName - [-DefaultProfile ] [] + [-ExpandActiveDirectoryAdministrator] [-DefaultProfile ] [] ``` ## DESCRIPTION @@ -288,6 +289,90 @@ InstancePoolName : This command gets information about the instance named managedInstance1. +### Example 8: Get all instances assigned to a resource group with external administrator information +```powershell +PS C:\> $val = Get-AzSqlInstance -ResourceGroupName "ResourceGroup01" -ExpandActiveDirectoryAdministrator +Location : westcentralus +Id : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Sql/managedInstances/managedInstance1 +ResourceGroupName : resourcegroup01 +ManagedInstanceName : managedInstance1 +Tags : +Identity : Microsoft.Azure.Management.Sql.Models.ResourceIdentity +Sku : Microsoft.Azure.Management.Internal.Resources.Models.Sku +FullyQualifiedDomainName : managedInstance1.wcusxxxxxxxxxxxxx.database.windows.net +AdministratorLogin : adminLogin1 +AdministratorPassword : +SubnetId : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Network/virtualNetworks/vnet_name/subnets/subnet_name +LicenseType : BasePrice +VCores : 8 +StorageSizeInGB : 512 +Administrators : Microsoft.Azure.Management.Sql.Models.ManagedInstanceExternalAdministrator + +Location : westcentralus +Id : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Sql/managedInstances/managedInstance2 +ResourceGroupName : resourcegroup01 +ManagedInstanceName : managedInstance2 +Tags : +Identity : Microsoft.Azure.Management.Sql.Models.ResourceIdentity +Sku : Microsoft.Azure.Management.Internal.Resources.Models.Sku +FullyQualifiedDomainName : managedInstance2.wcusxxxxxxxxxxxxx.database.windows.net +AdministratorLogin : adminLogin2 +AdministratorPassword : +SubnetId : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Network/virtualNetworks/vnet_name/subnets/subnet_name +LicenseType : BasePrice +VCores : 8 +StorageSizeInGB : 512 +DnsZone : ad35cna0mw +Administrators : Microsoft.Azure.Management.Sql.Models.ManagedInstanceExternalAdministrator + +PS C:\> $val.Administrators +AdministratorType : ActiveDirectory +PrincipalType : Group +Login : Dummy +Sid : df7667b8-f9fd-4029-a0e3-b43c75ce9538 +TenantId : f553829b-6d84-481b-86a9-42db57c1dc73 +AzureADOnlyAuthentication : True + +AdministratorType : ActiveDirectory +PrincipalType : Group +Login : Dummy2 +Sid : df7667b8-f9fd-4029-a0e3-b43c75ce9538 +TenantId : f553829b-6d84-481b-86a9-42db57c1dc73 +AzureADOnlyAuthentication : True +``` + +This command gets information about all instances assigned to the resource group ResourceGroup01. . + +### Example 9: Get information about an instance with external administrator information +```powershell +PS C:\> $val = Get-AzSqlInstance -Name "managedInstance1" -ResourceGroupName "ResourceGroup01" -ExpandActiveDirectoryAdministrator +Location : westcentralus +Id : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Sql/managedInstances/managedInstance1 +ResourceGroupName : resourcegroup01 +ManagedInstanceName : managedInstance1 +Tags : +Identity : Microsoft.Azure.Management.Sql.Models.ResourceIdentity +Sku : Microsoft.Azure.Management.Internal.Resources.Models.Sku +FullyQualifiedDomainName : managedInstance1.wcusxxxxxxxxxxxxx.database.windows.net +AdministratorLogin : adminLogin1 +AdministratorPassword : +SubnetId : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Network/virtualNetworks/vnet_name/subnets/subnet_name +LicenseType : BasePrice +VCores : 8 +StorageSizeInGB : 512 +DnsZone : ad35cna0mw +Administrators : Microsoft.Azure.Management.Sql.Models.ManagedInstanceExternalAdministrator + +PS C:\> $val.Administrators +AdministratorType : ActiveDirectory +PrincipalType : Group +Login : Dummy +Sid : df7667b8-f9fd-4029-a0e3-b43c75ce9538 +TenantId : f553829b-6d84-481b-86a9-42db57c1dc73 +AzureADOnlyAuthentication : True +``` +This command gets information about the instance named managedInstance1. + ## PARAMETERS ### -DefaultProfile @@ -305,6 +390,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ExpandActiveDirectoryAdministrator +Expand Active Directory Administrator Information on the server. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -InstancePool The instance pool parent object. diff --git a/src/Sql/Sql/help/Get-AzSqlServer.md b/src/Sql/Sql/help/Get-AzSqlServer.md index 856eecf72f5b..eaa7d1657253 100644 --- a/src/Sql/Sql/help/Get-AzSqlServer.md +++ b/src/Sql/Sql/help/Get-AzSqlServer.md @@ -14,7 +14,7 @@ Returns information about SQL Database servers. ## SYNTAX ``` -Get-AzSqlServer [[-ResourceGroupName] ] [[-ServerName] ] +Get-AzSqlServer [[-ResourceGroupName] ] [[-ServerName] ] [-ExpandActiveDirectoryAdministrator] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` @@ -128,6 +128,74 @@ FullyQualifiedDomainName : server02.database.windows.net This command gets information about all the Azure SQL Database servers assigned to the resource group ResourceGroup01 that start with "server". +### Example 5: Get all instances of SQL Server assigned to a resource group with external administrator information +``` +PS C:\>$val = Get-AzSqlServer -ResourceGroupName "ResourceGroup01" -ExpandActiveDirectoryAdministrator +ResourceGroupName : resourcegroup01 +ServerName : server01 +Location : Central US +SqlAdministratorLogin : adminLogin +SqlAdministratorPassword : +ServerVersion : 12.0 +Tags : +Identity : +FullyQualifiedDomainName : server01.database.windows.net +Administrators : Microsoft.Azure.Management.Sql.Models.ServerExternalAdministrator + +ResourceGroupName : resourcegroup01 +ServerName : server02 +Location : West US +SqlAdministratorLogin : adminLogin +SqlAdministratorPassword : +ServerVersion : 12.0 +Tags : +Identity : +FullyQualifiedDomainName : server02.database.windows.net +Administrators : Microsoft.Azure.Management.Sql.Models.ServerExternalAdministrator + +PS C:\>$val.Administrators +AdministratorType : ActiveDirectory +PrincipalType : Group +Login : Dummy +Sid : df7667b8-f9fd-4029-a0e3-b43c75ce9538 +TenantId : f553829b-6d84-481b-86a9-42db57c1dc73 +AzureADOnlyAuthentication : True + +AdministratorType : ActiveDirectory +PrincipalType : Group +Login : Dummy2 +Sid : df7667b8-f9fd-4029-a0e3-b43c75ce9538 +TenantId : f553829b-6d84-481b-86a9-42db57c1dc73 +AzureADOnlyAuthentication : True +``` + +This command gets information about all the Azure SQL Database servers assigned to the resource group ResourceGroup01. + +### Example 6: Get information about an Azure SQL Database server with external administrator information +``` +PS C:\>$val = Get-AzSqlServer -ResourceGroupName "ResourceGroup01" -ServerName "Server01" -ExpandActiveDirectoryAdministrator +ResourceGroupName : resourcegroup01 +ServerName : server01 +Location : Central US +SqlAdministratorLogin : adminLogin +SqlAdministratorPassword : +ServerVersion : 12.0 +Tags : +Identity : +FullyQualifiedDomainName : server01.database.windows.net +Administrators : Microsoft.Azure.Management.Sql.Models.ServerExternalAdministrator + +PS C:\>$val.Administrators +AdministratorType : ActiveDirectory +PrincipalType : Group +Login : Dummy +Sid : df7667b8-f9fd-4029-a0e3-b43c75ce9538 +TenantId : f553829b-6d84-481b-86a9-42db57c1dc73 +AzureADOnlyAuthentication : True +``` + +This command gets information about the Azure SQL Database server named Server01. + ## PARAMETERS ### -DefaultProfile @@ -145,6 +213,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ExpandActiveDirectoryAdministrator +Expand Active Directory Administrator Information on the server. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -ResourceGroupName Specifies the name of the resource group to which servers are assigned. diff --git a/src/Sql/Sql/help/New-AzSqlInstance.md b/src/Sql/Sql/help/New-AzSqlInstance.md index b24f21482606..440f1ebd7bd9 100644 --- a/src/Sql/Sql/help/New-AzSqlInstance.md +++ b/src/Sql/Sql/help/New-AzSqlInstance.md @@ -14,43 +14,47 @@ Creates an Azure SQL Database Managed Instance. ### NewByEditionAndComputeGenerationParameterSet (Default) ``` -New-AzSqlInstance [-Name] [-ResourceGroupName] -AdministratorCredential +New-AzSqlInstance [-Name] [-ResourceGroupName] [-AdministratorCredential ] -Location -SubnetId [-LicenseType ] [-StorageSizeInGB ] -VCore -Edition -ComputeGeneration [-Collation ] [-PublicDataEndpointEnabled] [-ProxyOverride ] [-TimezoneId ] [-Tag ] [-AssignIdentity] [-DnsZonePartner ] [-InstancePoolName ] [-MinimalTlsVersion ] [-BackupStorageRedundancy ] [-MaintenanceConfigurationId ] [-AsJob] [-Force] + [-EnableActiveDirectoryOnlyAuthentication] [-ExternalAdminName ] [-ExternalAdminSID ] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` ### NewByInstancePoolParentObjectParameterSet ``` New-AzSqlInstance [-InstancePool] [-Name] - -AdministratorCredential [-StorageSizeInGB ] -VCore [-Collation ] + [-AdministratorCredential ] [-StorageSizeInGB ] -VCore [-Collation ] [-PublicDataEndpointEnabled] [-ProxyOverride ] [-TimezoneId ] [-Tag ] [-AssignIdentity] [-DnsZonePartner ] [-MinimalTlsVersion ] [-BackupStorageRedundancy ] - [-MaintenanceConfigurationId ] [-AsJob] [-Force] [-DefaultProfile ] [-WhatIf] + [-MaintenanceConfigurationId ] [-AsJob] [-Force] [-EnableActiveDirectoryOnlyAuthentication] + [-ExternalAdminName ] [-ExternalAdminSID ] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` ### NewByInstancePoolResourceIdParameterSet ``` -New-AzSqlInstance [-InstancePoolResourceId] [-Name] -AdministratorCredential +New-AzSqlInstance [-InstancePoolResourceId] [-Name] [-AdministratorCredential ] [-StorageSizeInGB ] -VCore [-Collation ] [-PublicDataEndpointEnabled] [-ProxyOverride ] [-TimezoneId ] [-Tag ] [-AssignIdentity] [-DnsZonePartner ] [-MinimalTlsVersion ] [-BackupStorageRedundancy ] - [-MaintenanceConfigurationId ] [-AsJob] [-Force] [-DefaultProfile ] [-WhatIf] + [-MaintenanceConfigurationId ] [-AsJob] [-Force] [-EnableActiveDirectoryOnlyAuthentication] + [-ExternalAdminName ] [-ExternalAdminSID ] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` ### NewBySkuNameParameterSetParameter ``` -New-AzSqlInstance [-Name] [-ResourceGroupName] -AdministratorCredential +New-AzSqlInstance [-Name] [-ResourceGroupName] [-AdministratorCredential ] -Location -SubnetId [-LicenseType ] [-StorageSizeInGB ] -VCore -SkuName [-Collation ] [-PublicDataEndpointEnabled] [-ProxyOverride ] [-TimezoneId ] [-Tag ] [-AssignIdentity] [-DnsZonePartner ] [-InstancePoolName ] [-MinimalTlsVersion ] [-BackupStorageRedundancy ] - [-MaintenanceConfigurationId ] [-AsJob] [-Force] [-DefaultProfile ] [-WhatIf] + [-MaintenanceConfigurationId ] [-AsJob] [-Force] [-EnableActiveDirectoryOnlyAuthentication] + [-ExternalAdminName ] [-ExternalAdminSID ] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` @@ -206,6 +210,57 @@ MaintenanceConfigurationId : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx This command creates a new instance with maintenance configuration MI_2 +### Example 7: Create a new instance with External(Azure Active Directory) Administrator, Azure Active Directory Only Authentication and no SqlAdministratorCredentials +```powershell +PS C:\>New-AzSqlInstance -Name managedInstance2 -ResourceGroupName ResourceGroup01 -ExternalAdminName DummyLogin -EnableActiveDirectoryOnlyAuthentication -Location westcentralus -SubnetId "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Network/virtualNetworks/vnet_name/subnets/subnet_name" -LicenseType LicenseIncluded -StorageSizeInGB 1024 -VCore 16 -Edition "GeneralPurpose" -ComputeGeneration Gen4 +Location : westcentralus +Id : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Sql/managedInstances/managedInstance1 +ResourceGroupName : resourcegroup01 +ManagedInstanceName : managedInstance2 +Tags : +Identity : Microsoft.Azure.Management.Sql.Models.ResourceIdentity +Sku : Microsoft.Azure.Management.Internal.Resources.Models.Sku +FullyQualifiedDomainName : managedInstance1.wcusxxxxxxxxxxxxx.database.windows.net +AdministratorLogin : adminLogin1 +AdministratorPassword : +SubnetId : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Network/virtualNetworks/vnet_name/subnets/subnet_name +LicenseType : LicenseIncluded +VCores : 16 +StorageSizeInGB : 1024 +DnsZone : ad35cna0mw +InstancePoolName : +Administrators : + +PS C:\>$val = Get-AzSqlInstance -Name managedInstance2 -ResourceGroupName ResourceGroup01 -ExpandActiveDirectoryAdministrator +Location : westcentralus +Id : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Sql/managedInstances/managedInstance1 +ResourceGroupName : resourcegroup01 +ManagedInstanceName : managedInstance2 +Tags : +Identity : Microsoft.Azure.Management.Sql.Models.ResourceIdentity +Sku : Microsoft.Azure.Management.Internal.Resources.Models.Sku +FullyQualifiedDomainName : managedInstance1.wcusxxxxxxxxxxxxx.database.windows.net +AdministratorLogin : adminLogin1 +AdministratorPassword : +SubnetId : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcegroup01/providers/Microsoft.Network/virtualNetworks/vnet_name/subnets/subnet_name +LicenseType : LicenseIncluded +VCores : 16 +StorageSizeInGB : 1024 +DnsZone : ad35cna0mw +InstancePoolName : +Administrators : Microsoft.Azure.Management.Sql.Models.ManagedInstanceExternalAdministrator + +PS C:\>$val.Administrators +AdministratorType : ActiveDirectory +PrincipalType : Group +Login : DummyLogin +Sid : df7667b8-f9fd-4029-a0e3-b43c75ce9538 +TenantId : f553829b-6d84-481b-86a9-42db57c1dc73 +AzureADOnlyAuthentication : True +``` + +This command creates a new instance with external administrator properties and azure active directory only authentication enabled. + ## PARAMETERS ### -AdministratorCredential @@ -216,7 +271,7 @@ Type: System.Management.Automation.PSCredential Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -344,6 +399,51 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -EnableActiveDirectoryOnlyAuthentication +Enable Active Directory Only Authentication on the server. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExternalAdminName +Specifies the display name of the user, group or application which is the Azure Active Directory administrator for the server. This display name must exist in the active directory associated with the current subscription. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExternalAdminSID +Specifies the object ID of the user, group or application which is the Azure Active Directory administrator. + +```yaml +Type: System.Nullable`1[System.Guid] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Force Skip confirmation message for performing the action diff --git a/src/Sql/Sql/help/New-AzSqlServer.md b/src/Sql/Sql/help/New-AzSqlServer.md index a6c808e6e0eb..30aee0e2b9b5 100644 --- a/src/Sql/Sql/help/New-AzSqlServer.md +++ b/src/Sql/Sql/help/New-AzSqlServer.md @@ -14,9 +14,10 @@ Creates a SQL Database server. ## SYNTAX ``` -New-AzSqlServer -ServerName -SqlAdministratorCredentials -Location +New-AzSqlServer -ServerName [-SqlAdministratorCredentials ] -Location [-Tags ] [-ServerVersion ] [-AssignIdentity] [-PublicNetworkAccess ] - [-MinimalTlsVersion ] [-AsJob] [-ResourceGroupName] + [-MinimalTlsVersion ] [-AsJob] [-EnableActiveDirectoryOnlyAuthentication] + [-ExternalAdminName ] [-ExternalAdminSID ] [-ResourceGroupName] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` @@ -39,6 +40,39 @@ Tags : This command creates a version 12 Azure SQL Database server. +### Example 2: Create a new Azure SQL Database server with External(Azure Active Directory) Administrator, Azure Active Directory Only Authentication and no SqlAdministratorCredentials +``` +PS C:\>New-AzSqlServer -ResourceGroupName "ResourceGroup01" -Location "Central US" -ServerName "server01" -ServerVersion "12.0" -ExternalAdminName DummyLogin -EnableActiveDirectoryOnlyAuthentication +ResourceGroupName : resourcegroup01 +ServerName : server01 +Location : Central US +SqlAdministratorLogin : adminLogin +SqlAdministratorPassword : +ServerVersion : 12.0 +Tags : +Administrators : + +PS C:\>$val = Get-AzSqlServer -ResourceGroupName "ResourceGroup01" -ServerName "server01" -ExpandActiveDirectoryAdministrator +ResourceGroupName : resourcegroup01 +ServerName : server01 +Location : Central US +SqlAdministratorLogin : randomLogin +SqlAdministratorPassword : +ServerVersion : 12.0 +Tags : +Administrators : Microsoft.Azure.Management.Sql.Models.ServerExternalAdministrator + +PS C:\>$val.Administrators +AdministratorType : ActiveDirectory +PrincipalType : Group +Login : DummyLogin +Sid : df7667b8-f9fd-4029-a0e3-b43c75ce9538 +TenantId : f553829b-6d84-481b-86a9-42db57c1dc73 +AzureADOnlyAuthentication : True +``` + +This command creates a version 12 Azure SQL Database server with external administrator properties and azure active directory only authentication enabled. + ## PARAMETERS ### -AsJob @@ -86,6 +120,51 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -EnableActiveDirectoryOnlyAuthentication +Enable Active Directory Only Authentication on the server. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExternalAdminName +Specifies the display name of the user, group or application which is the Azure Active Directory administrator for the server. This display name must exist in the active directory associated with the current subscription. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExternalAdminSID +Specifies the object ID of the user, group or application which is the Azure Active Directory administrator. + +```yaml +Type: System.Nullable`1[System.Guid] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Location Specifies the location of the data center where this cmdlet creates the server. @@ -189,7 +268,7 @@ Type: System.Management.Automation.PSCredential Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False