教程:使用自动化为 SQL Server 设置 Microsoft Entra 管理员
- 项目
适用于: SQL Server 2022 (16.x)
注意
此功能在 SQL Server 2022 (16.x) 或更高版本中可用,并且仅支持用于 Windows 和 Linux 主机上的本地 SQL Server 以及 Windows Azure VM 上的 SQL Server 2022。
在本文中,我们将介绍如何设置 Microsoft Entra 管理员,以便使用 Azure 门户和例如下列 API SQL Server 进行 Microsoft Entra ID(以前称为 Azure Active Directory)身份验证:
- PowerShell
- Azure CLI
- ARM 模板
我们还将介绍在 Azure 门户中设置 SQL Server 的 Microsoft Entra 管理员这一更新功能,该功能可自动创建证书和注册应用程序。 以前,设置 SQL Server 的 Microsoft Entra 身份验证需要通过 Azure 证书和应用程序注册手动设置 Microsoft Entra 管理员。
注意
虽然 Microsoft Entra ID 是 Azure Active Directory (Azure AD) 的新名称,但为了防止中断现有环境,Azure AD 仍保留在一些硬编码的元素中,例如 UI 字段、连接提供程序、错误代码和 cmdlet。 在本文中,这两个名称是可互换的。
先决条件
- 已安装 SQL Server 2022 (16.x) 或更高版本。
- SQL Server 已连接到 Azure 云。 有关详细信息,请参阅将 SQL Server 连接到 Azure Arc。
- Microsoft Entra ID 配置为在与 Azure Arc 实例相同的租户中进行身份验证。
- 需要 Azure Key Vault。
设置 Microsoft Entra 管理员之前的准备
在 SQL Server – Azure Arc 和密钥保管库资源中设置 Microsoft Entra 管理员需要以下权限。
配置 Azure Arc 的权限
按照指南操作,确保 SQL Server 已连接到 Azure Arc。为 SQL Server – Azure Arc 资源设置 Microsoft Entra 管理员的用户应具有服务器的“参与者”角色。
- 转到 Azure 门户
- 选择“SQL Server – Azure Arc”,然后选择 SQL Server 主机的实例。
- 选择“访问控制(IAM)”。
- 选择“添加”>“添加角色分配”为设置 Microsoft Entra 管理员的用户添加“参与者”角色。
配置 Azure Key Vault 的权限
如果还没有 Azure Key Vault,请创建 Azure Key Vault。 设置 Microsoft Entra 管理员的用户应具有 Azure 密钥保管库的“参与者”角色。 要为 Azure Key Vault 中的用户添加角色,请执行以下操作:
- 转到 Azure 门户
- 转到密钥保管库资源。
- 选择“访问控制 (IAM)”。
- 选择“添加”>“添加角色分配”为设置 Microsoft Entra 管理员的用户添加“参与者”角色。
设置 SQL Server 主机访问策略
在 Azure 门户导航到 Azure Key Vault 实例并选择“访问策略”。
选择“添加访问策略”。
对于“密钥权限”,请使用“Sign”。
对于“机密权限”,请选择“获取”和“列出”。
对于“证书权限”,请选择“获取”和“列出”。
选择下一步。
在“主体”页上,搜索你计算机的名称 - Azure Arc 实例,是 SQL Server 主机的主机名。
通过选择两次“下一步”或选择“审查 + 创建”,跳过“应用程序(可选)”。
验证“主体”的“对象 ID”匹配分配给实例的托管标识的“主体 ID”。
若要确认,请转到资源页并在“概览”页的“Essentials”框的右上角选择“JSON 视图”。 在标识下会找到“principalId”
选择创建。
必须选择“创建”以确保应用权限。 若要确保已存储权限,请刷新浏览器窗口并检查 Azure Arc 实例的行是否仍然存在。
为 Microsoft Entra 用户设置访问策略
- 在 Azure 门户导航到 Azure Key Vault 实例并选择“访问策略”。
- 选择“添加访问策略”。
- 对于“密钥权限”,请选择“获取”、“列出”和“创建”。
- 对于“机密权限”,请选择“获取”、“列出”和“设置”。
- 对于“证书权限”,请选择“获取”、“列出”和“创建”。
- 对于“选择主体”,请添加要用于连接到 SQL Server 的 Microsoft Entra 用户。
- 依次选择“添加”、“保存”。
为 SQL Server 设置 Microsoft Entra 管理员
借助全新 API 和门户功能,用户可以为 SQL Server 设置 Microsoft Entra 管理员,无需单独创建 Azure 证书和 Microsoft Entra 应用程序。 选择一个选项卡,了解如何通过自动创建证书和应用程序为连接到 Azure Arc 的 SQL Server 设置 Microsoft Entra 管理员。
注意
在设置 Microsoft Entra 管理员之前,ARM 模板仍需创建 Azure 密钥保管库证书和 Microsoft Entra 应用程序。有关此过程的更多信息,请参阅“教程:为 SQL Server 设置 Microsoft Entra 身份验证”。
使用 Azure 门户设置 Microsoft Entra 管理员,在同一过程中创建 Azure 密钥保管库证书和 Microsoft Entra 应用程序。 在 SQL Server 中使用 Microsoft Entra 身份验证需要完成上述操作。
注意
以前,在设置 Microsoft Entra 管理员之前,需要注册 Azure 密钥保管库证书和 Microsoft Entra 应用程序。 虽然现在不再需要这样做,但用户仍可以选择提供自己的证书和应用程序来设置 Microsoft Entra 管理员。
使用 Azure 门户 设置 Microsoft Entra 管理员
转到 Azure 门户,选择“SQL Server – Azure Arc”,然后选择 SQL Server 主机的实例。
检查“SQL Server - Azure Arc”资源的状态,并查看它是否已通过转到“属性”菜单进行连接。 有关详细信息,请参阅验证已启用 Arc 的 SQL Server 资源。
从“资源”菜单的“设置”下选择“Microsoft Entra ID 和 Purview”。
选择“设置管理员”,打开“Microsoft Entra ID”窗格,然后选择将作为管理员登录添加到 SQL Server 的帐户。
选择“服务托管证书”。
选择“更改密钥保管库”,然后选择现有 Azure Key Vault 资源。
选择“服务托管的应用注册”。
选择“保存”。 这会向 Arc 服务器代理发送请求,该代理将为该 SQL Server 实例配置 Microsoft Entra 身份验证。 该操作可能需要几分钟才能完成;尝试 Microsoft Entra 登录之前,请等待保存过程确认为
Saved successfully
。服务托管的应用注册为你执行以下操作:
- 在密钥保管库中创建名称格式为
<hostname>-<instanceName><uniqueNumber>
的证书。 - 创建名称为
<hostname>-<instanceName><uniqueNumber>
等的 Microsoft Entra 应用程序,并为该应用程序分配必要的权限。 有关详细信息,请参阅授予应用程序权限。 - 为应用程序分配 Azure 密钥保管库中的新证书。
- 将这些设置保存到 Azure Arc。
- 在密钥保管库中创建名称格式为
注意
为 Microsoft Entra 创建的证书不会自动轮换。 客户可以选择提供自己的证书和应用程序设置 Microsoft Entra 管理员。 有关更多信息,请参阅“教程:为 SQL Server 设置 Microsoft Entra 身份验证”。
以下 Azure CLI 脚本设置了 Microsoft Entra 管理员并创建了 Azure 密钥保管库证书和 Microsoft Entra 应用程序。 其他部分提供了一个证书和应用程序已存在时用于设置 Microsoft Entra 管理员的示例脚本。
注意
为 Microsoft Entra 设置创建的证书不会自动轮换。
- 需要 Azure CLI 版本 2.37.0 或更高版本
- 需要 Az.ConnectedMachine 0.5.1 或更高版本
要安装 Az.ConnectedMachine
模块,请使用 az extension add --name ConnectedMachine
。 要检查安装的 Azure CLI 版本,请使用 az version
。
Azure CLI 脚本使用以下输入参数:
<applicationName>
- 将创建的应用程序名称<certSubjectName>
- 将创建的证书名称<keyVaultName>
- 密钥保管库名称。 运行脚本之前必须创建此密钥保管库<machineName>
- SQL Server 主机的计算机名称<resourceGroupName>
- 包含 SQL Server - Azure Arc 实例的资源组名称<adminAccountName>
– 要为 SQL Server 设置的 Microsoft Entra 管理员帐户<instanceName>
- SQL Server 命名实例可选参数。 如有命名实例,请使用此参数。 如果省略,则使用默认名称MSSQLSERVER
。<tenantId>
- 租户 ID 可选参数。 可以通过转到 Azure 门户,然后转到 Microsoft Entra ID 资源来找到租户 ID。 在“概述”窗格中,应会看到“租户 ID”。 如果省略,则将默认租户 ID 用作参数<subscriptionId>
- 订阅 ID 可选参数。 可以在 Azure 门户中找到订阅 ID。 如果省略,则使用默认订阅 ID
要使用以下 Azure CLI 脚本,请将脚本保存为 .ps1
文件,并运行以下命令:
./aadAzCliSetup.ps1 -applicationName "<applicationName>" -certSubjectName "<certSubjectName>" -keyVaultName "<keyVaultName>" -machineName "<machineName>" -resourceGroupName "<resourceGroupName>" -adminAccountName "<adminAccountName>" -instanceName "<instanceName>" -tenantId "<tenantId>" -subscriptionId "<subscriptionId>"
Azure CLI 脚本
注意
对于 Linux 主机上的 SQL Server,请将脚本中的 WindowsAgent.SqlServer
替换为 LinuxAgent.SqlServer
。
# AZ CLI and AZ CLI's connected machine extension must be installed before running this script
param (
[Parameter(mandatory=$true)] $applicationName,
[Parameter(mandatory=$true)] $certSubjectName,
[Parameter(mandatory=$true)] $keyVaultName,
[Parameter(mandatory=$true)] $machineName,
[Parameter(mandatory=$true)] $resourceGroupName,
[Parameter(mandatory=$true)] $adminAccountName,
$instanceName,
$tenantId,
$subscriptionId
)
# Constants
#
$NUMRETRIES = 60
# Helper functions
#
function ConvertFrom-StringArray {
param (
[string[]] $stringArray
)
if (!$stringArray)
{
return $null
}
else
{
return ConvertFrom-JSON ($stringArray -join "`n")
}
}
# Check parameters
#
if ([string]::IsNullOrEmpty($instanceName))
{
Write-Host "Warning: SQL Instance name (-instanceName) not provided. Default of MSSQLSERVER will be used"
$instanceName = "MSSQLSERVER"
}
$tenantIdArgument = ""
if ([string]::IsNullOrEmpty($tenantId))
{
Write-Host "Warning: Tenant ID (-tenantId) not supplied to the script, so default tenant is being used"
}
else
{
$tenantIdArgument = "-TenantId '" + $tenantId + "'"
}
$subscriptionIdArgument = ""
if ([string]::IsNullOrEmpty($subscriptionId))
{
Write-Host "Warning: Subscription ID (-subscriptionId) not supplied to the script, so default subscription is being used"
}
else
{
$subscriptionIdArgument = "-SubscriptionId '" + $subscriptionId + "'"
}
# Login and select subscription
#
$login = az login --tenant $tenantId --use-device-code
if (!$login)
{
Write-Error "Login to Azure AD failed. Exiting."
exit 1
}
if ($subscriptionId)
{
az account set -s $subscriptionId
}
$accountInfo = ConvertFrom-StringArray (az account show)
if (!$accountInfo)
{
Write-Error "Cannot query logged in Azure AD account. Check that 'az login' and 'az account set' succeeded"
exit 1
}
if ($subscriptionId)
{
if ($subscriptionId.ToLower() -ne $accountInfo.id.ToLower())
{
Write-Error "Could not select the desired subscription"
exit 1
}
}
else
{
$subscriptionId = $accountInfo.id
}
# Check AKV path exists
#
$keyVault = ConvertFrom-StringArray (az keyvault show --name $keyVaultName)
if (!$keyVault)
{
Write-Error "Azure key vault '$keyVaultName' does not exist"
exit 1
}
# Check certificate doesn't exist
#
$cert = ConvertFrom-StringArray (az keyvault certificate show --name $certSubjectName --vault-name $keyVaultName 2>$null)
if ($cert)
{
Write-Error "Certificate '$certSubjectName' already exists in key vault '$keyVaultName'"
exit 1
}
# Check app registration doesn't exist
#
$applications = ConvertFrom-StringArray (az ad app list --display-name $applicationName --only-show-errors)
if ($applications.length -gt 0)
{
Write-Error "App registration with name '$applicationName' already exists"
exit 1
}
# Check Arc SQL instance is valid
#
$extension = ConvertFrom-StringArray (az connectedmachine extension show --machine-name $machineName --name "WindowsAgent.SqlServer" --resource-group $resourceGroupName)
if (!$extension)
{
Write-Error "SQL Server Arc Server not found for machine '$machineName' in resource group '$resourceGroupName'"
exit 1
}
$arcServicePrincipals = ConvertFrom-StringArray(az ad sp list --display-name $machineName --only-show-errors)
if (!$arcServicePrincipals -or $arcServicePrincipals.length -eq 0)
{
Write-Error "Could not find a service principal account with the name '$machineName'"
exit 1
}
else
{
$principalFound = $false
for ($i = 0; $i -lt $arcServicePrincipals.length; $i++)
{
if ($arcServicePrincipals[$i].displayName.toLower() -eq $machineName.toLower()) {
if ($principalFound) {
Write-Error "Could not find exactly one service principal account with the name '$machineName'"
exit 1
}
$arcServicePrincipal = $arcServicePrincipals[$i]
$principalFound = $true
}
}
if (!$principalFound) {
Write-Error "Could not find a service principal account with the name '$machineName'"
exit 1
}
}
# Check if admin account exists
#
$adminAccount = ConvertFrom-StringArray (az ad user show --id $adminAccountName --only-show-errors 2>$null)
$adminAccountType = 0
if (!$adminAccount)
{
$adminAccounts = ConvertFrom-StringArray (az ad user list --filter "mail eq '$adminAccountName'" --only-show-errors 2>$null)
if ($adminAccounts -and $adminAccounts.length -gt 0)
{
if ($adminAccounts.length -eq 1)
{
$adminAccount = $adminAccounts[0]
}
else
{
Write-Error "Multiple Azure AD accounts found with identifier '$adminAccountName'"
exit 1
}
}
else
{
$adminAccount = ConvertFrom-StringArray (az ad group show --group $adminAccountName --only-show-errors 2>$null)
if (!$adminAccount)
{
$adminAccounts = ConvertFrom-StringArray (az ad app list --display-name $adminAccountName --only-show-errors 2>$null)
if ($adminAccounts -and $adminAccounts.length -gt 0)
{
if ($adminAccounts.length -eq 1)
{
$adminAccount = $adminAccounts[0]
}
else
{
Write-Error "Multiple Azure AD applications found with identifier '$adminAccountName'"
exit 1
}
}
else
{
Write-Error "Admin account not found"
exit 1
}
}
else
{
$adminAccountType = 1
}
}
}
if ($adminAccount)
{
$adminAccountSid = $adminAccount.id
}
else
{
Write-Error "Admin account not found"
exit 1
}
# Create certificate in AKV
#
$keyVaultPolicy = ConvertFrom-StringArray (az keyvault certificate get-default-policy)
if (!$keyVaultPolicy)
{
Write-Error "Could not get default key vault policy"
exit 1
}
$keyVaultPolicy.x509CertificateProperties.subject = "CN=" + $certSubjectName
$policyString = (ConvertTo-JSON -Depth 8 $keyVaultPolicy).replace("`r`n", "")
$escapedPolicyString = $policyString.replace("`"", "\`"")
$cert = ConvertFrom-StringArray (az keyvault certificate create --vault-name $keyVaultName --name $certSubjectName --policy $escapedPolicyString)
if (!$cert)
{
Write-Error "Failed to create certificate '$certSubjectName'"
exit 1
}
# Wait until cert is created?
#
$cert = ConvertFrom-StringArray (az keyvault certificate show --vault-name $keyVaultName --name $certSubjectName)
for (($i = 0); $i -lt $NUMRETRIES -and (!$cert -or !$cert.attributes.enabled); $i++)
{
$cert = ConvertFrom-StringArray (az keyvault certificate show --vault-name $keyVaultName --name $certSubjectName)
if (!$cert -or !$cert.attributes.enabled)
{
Start-Sleep -Seconds 5
}
}
# Allow Arc to access AKV
#
$newPerms = ConvertFrom-StringArray (az keyvault set-policy --name $keyVaultName --secret-permissions get list --certificate-permissions get list --object-id $arcServicePrincipal.id)
if (!$newPerms)
{
Write-Host "Warning: Unable to add permissions to key vault '$keyVaultName' for Arc's service principal's identity '$($arcServicePrincipal.id)'. Arc may not be able to configure Azure AD authentication"
}
# Create an Azure AD application
#
$application = ConvertFrom-StringArray (az ad app create --display-name $applicationName --only-show-errors)
if (!$application)
{
Write-Error "Unable to create the app registration '$applicationName'"
exit 1
}
# Set perms on app registration
#
az ad app permission add --id $application.id --api 00000003-0000-0000-c000-000000000000 --api-permissions c79f8feb-a9db-4090-85f9-90d820caa0eb=Scope --only-show-errors # Delegated Application.Read.All
az ad app permission add --id $application.id --api 00000003-0000-0000-c000-000000000000 --api-permissions 0e263e50-5827-48a4-b97c-d940288653c7=Scope --only-show-errors # Delegated Directory.AccessAsUser.All
az ad app permission add --id $application.id --api 00000003-0000-0000-c000-000000000000 --api-permissions 7ab1d382-f21e-4acd-a863-ba3e13f7da61=Role --only-show-errors # Application Directory.Read.All
az ad app permission add --id $application.id --api 00000003-0000-0000-c000-000000000000 --api-permissions 5f8c59db-677d-491f-a6b8-5f174b11ec1d=Scope --only-show-errors # Delegated Group.Read.All
az ad app permission add --id $application.id --api 00000003-0000-0000-c000-000000000000 --api-permissions a154be20-db9c-4678-8ab7-66f6cc099a59=Scope --only-show-errors # Delegated User.Read.All
# Upload cert to Azure AD
#
$certUploadRes = ConvertFrom-StringArray (az ad app credential reset --id $application.id --cert $certSubjectName --keyvault $keyVaultName --append --only-show-errors)
if (!$certUploadRes)
{
Write-Error "Failed to set certificate '$certSubjectName' as a credential for app registration '$applicationName'"
exit 1
}
# Remove the version from the secret ID if present
#
$secretId = $cert.sid
if ($secretId -Match "(https:\/\/[^\/]+\/secrets\/[^\/]+)(\/.*){0,1}$") {
if ($Matches[1]) {
$secretId = $Matches[1]
}
}
# Create the settings object to write to the Azure extension for SQL Server
#
$instanceSettings = @{
instanceName = $instanceName
adminLoginName = $adminAccountName
adminLoginSid = $adminAccountSid
azureCertSecretId = $secretId
azureCertUri = $cert.id
azureKeyVaultResourceUID = $keyVault.id
managedCertSetting = "CUSTOMER MANAGED CERT"
managedAppSetting = "CUSTOMER MANAGED APP"
appRegistrationName = $application.displayName
appRegistrationSid = $application.appId
tenantId = $tenantId
aadCertSubjectName = $certSubjectName
adminLoginType = $adminAccountType
}
$extension = ConvertFrom-StringArray (az connectedmachine extension show --machine-name $machineName --name "WindowsAgent.SqlServer" --resource-group $resourceGroupName)
if ($extension.properties.Settings.AzureAD)
{
$aadSettings = $extension.properties.Settings.AzureAD
$instanceFound = $false
$instanceNameLower = $instanceName.ToLower()
$instanceIndex = 0
for (($i = 0); $i -lt $aadSettings.Length; $i++)
{
if ($aadSettings[$i].instanceName.ToLower() -eq $instanceNameLower)
{
$instanceIndex = $i
$instanceFound = $true
break
}
}
if ($instanceFound)
{
$aadSettings[$instanceIndex] = $instanceSettings
}
else
{
$aadSettings += $instanceSettings
}
$extension.properties.Settings.AzureAD = $aadSettings
}
else
{
$aadSettings = , $instanceSettings
$extension.properties.Settings | Add-Member -Name 'AzureAD' -Value $aadSettings -MemberType NoteProperty
}
$settingsString = (ConvertTo-Json $extension.properties.Settings).replace("`"", "\`"").replace("`r`n", "")
# Push settings to Arc
#
Write-Host "Writing Azure AD setting to Azure extension for SQL Server. This may take several minutes..."
$updateRes = az connectedmachine extension update --machine-name $machineName --name "WindowsAgent.SqlServer" --resource-group $resourceGroupName --settings $settingsString
if (!$updateRes)
{
Write-Error "Failed to update Azure extension for SQL Server with Azure AD settings"
exit 1
}
Write-Output "Success"
运行脚本可能需要几分钟。 完成运行后,会显示类似于以下内容的消息:
Name Location ProvisioningState
---- -------- -----------------
WindowsAgent.SqlServer westus2 Succeeded
Success
通过 Azure CLI 使用现有证书和应用程序设置 Microsoft Entra 管理员
如果已有现有的 Azure 密钥保管库证书与要用于设置 Microsoft Entra 管理员的 Azure 应用程序,可以使用以下 CLI 脚本:
# Set up Microsoft Entra admin for user's existing key vault, certificate, and application
# Requires input parameters indicated below
# Connect statement
AZ Login
#Input parameters
$subscriptionId="<subscriptionId>"
$tenantId="<tenantId>"
$machineName="<machineName>" # hostname
$instanceName="<instanceName>" # SQL Server is define as `machine_name\instance_name`
$resourceGroupName="<resourceGroupName>"
$keyVaultName="<keyVaultName>"
$certSubjectName="<certSubjectName>" # Your existing certificate name
$applicationName="<applicationName>" # Your existing application name
$adminAccountName="<adminAccountName>"
$adminAccountSid="<adminID>" # Use object ID for the Azure AD user and group, or client ID for the Azure AD application
$adminAccountType= 0 # 0 – for Azure AD user and application, 1 for Azure AD group
# Helper function
#
function ConvertFrom-StringArray {
param (
[string[]] $stringArray
)
if (!$stringArray)
{
return $null
}
else
{
return ConvertFrom-JSON ($stringArray -join "`n")
}
}
$keyVault = ConvertFrom-StringArray (az keyvault show --name $keyVaultName)
if (!$keyVault)
{
Write-Error "Azure key vault '$keyVaultName' does not exist"
exit 1
}
$cert = ConvertFrom-StringArray (az keyvault certificate show --name $certSubjectName --vault-name $keyVaultName 2>$null)
if (!$cert)
{
Write-Error "Supplied certificate $certSubjectName was not found for this key vault. Please specify an existing certficate"
exit 1
}
$secretId = $cert.sid
if ($secretId -Match "(https:\/\/[^\/]+\/secrets\/[^\/]+)(\/.*){0,1}$") {
if ($Matches[1]) {
$secretId = $Matches[1]
}
}
$application = ConvertFrom-StringArray (az ad app list --display-name $applicationName --only-show-errors)
if (!$application)
{
Write-Error "Supplied application was not found in the subscription. Please specify an existing application"
exit 1
}
# Create the settings object to write to the Arc extension
#
$instanceSettings = @{
instanceName = $instanceName
adminLoginName = $adminAccountName
adminLoginSid = $adminAccountSid
azureCertSecretId = $secretId
azureCertUri = $cert.id
azureKeyVaultResourceUID = $keyVault.id
managedCertSetting = "CUSTOMER MANAGED CERT"
managedAppSetting = "CUSTOMER MANAGED APP"
appRegistrationName = $application.displayName
appRegistrationSid = $application.appId
tenantId = $tenantId
aadCertSubjectName = $certSubjectName
adminLoginType = $adminAccountType
}
$extension = ConvertFrom-StringArray (az connectedmachine extension show --machine-name $machineName --name "WindowsAgent.SqlServer" --resource-group $resourceGroupName)
if ($extension.properties.Settings.AzureAD)
{
$aadSettings = $extension.properties.Settings.AzureAD
$instanceFound = $false
$instanceNameLower = $instanceName.ToLower()
$instanceIndex = 0
for (($i = 0); $i -lt $aadSettings.Length; $i++)
{
if ($aadSettings[$i].instanceName.ToLower() -eq $instanceNameLower)
{
$instanceIndex = $i
$instanceFound = $true
break
}
}
if ($instanceFound)
{
$aadSettings[$instanceIndex] = $instanceSettings
}
else
{
$aadSettings += $instanceSettings
}
$extension.properties.Settings.AzureAD = $aadSettings
}
else
{
$aadSettings = , $instanceSettings
$extension.properties.Settings | Add-Member -Name 'AzureAD' -Value $aadSettings -MemberType NoteProperty
}
$settingsString = (ConvertTo-Json $extension.properties.Settings).replace("`"", "\`"").replace("`r`n", "")
# Push settings to Arc
#
Write-Host "Writing Azure AD setting to SQL Server Arc Extension. This may take several minutes..."
$updateRes = az connectedmachine extension update --machine-name $machineName --name "WindowsAgent.SqlServer" --resource-group $resourceGroupName --settings $settingsString
if (!$updateRes)
{
Write-Error "Failed to update SQL Arc Extension with Azure AD settings"
exit 1
}
Write-Output "Success"
以下 PowerShell 脚本设置了 Microsoft Entra 管理员并创建了 Azure 密钥保管库证书和 Microsoft Entra 应用程序。 其他部分提供了一个证书和应用程序已存在时用于设置 Microsoft Entra 管理员的示例脚本。
注意
为 Microsoft Entra 设置创建的证书不会自动轮换。
完成本教程需要以下模块。 安装最新版本或高于下文所述的版本的模块:
- Az.Accounts 3.37.0
- Az.ConnectedMachine 0.5.0
- Az.KeyVault 4.5.0
- Az.Resources 6.0.0
PowerShell 脚本使用以下输入参数:
<applicationName>
- 将创建的应用程序名称<certSubjectName>
- 将创建的证书名称<keyVaultName>
- 密钥保管库名称。 运行脚本之前必须创建此密钥保管库<machineName>
- SQL Server 主机的计算机名称<resourceGroupName>
- 包含 SQL Server - Azure Arc 实例的资源组名称<adminAccountName>
– 要为 SQL Server 设置的 Microsoft Entra 管理员帐户<instanceName>
- SQL Server 命名实例可选参数。 如有命名实例,请使用此参数。 如果省略,则使用默认名称MSSQLSERVER
。<tenantId>
- 租户 ID 可选参数。 可以通过转到 Azure 门户,然后转到 Microsoft Entra ID 资源来找到租户 ID。 在“概述”窗格中,应会看到“租户 ID”。 如果省略,则将默认租户 ID 用作参数<subscriptionId>
- 订阅 ID 可选参数。 可以在 Azure 门户中找到订阅 ID。 如果省略,则使用默认订阅 ID
要使用以下 PowerShell 脚本,请将脚本保存为 .ps1
文件,并运行以下命令:
./aadPowerShellsetup.ps1 -applicationName "<applicationName>" -certSubjectName "<certSubjectName>" -keyVaultName "<keyVaultName>" -machineName "<machineName>" -resourceGroupName "<resourceGroupName>" -adminAccountName "<adminAccountName>" -instanceName "<instanceName>" -tenantId "<tenantId>" -subscriptionId "<subscriptionId>"
PowerShell 脚本
对于 Linux 主机上的 SQL Server,请将脚本中的 WindowsAgent.SqlServer
替换为 LinuxAgent.SqlServer
。
param (
[Parameter(mandatory=$true)] $applicationName,
[Parameter(mandatory=$true)] $certSubjectName,
[Parameter(mandatory=$true)] $keyVaultName,
[Parameter(mandatory=$true)] $machineName,
[Parameter(mandatory=$true)] $resourceGroupName,
[Parameter(mandatory=$true)] $adminAccountName,
$instanceName,
$tenantId,
$subscriptionId
)
Import-Module Az.Accounts
Import-Module Az.ConnectedMachine
Import-Module Az.KeyVault
Import-Module Az.Resources
# Constants
#
$NUMRETRIES = 60
# Check parameters
#
if ([string]::IsNullOrEmpty($instanceName))
{
Write-Host "Warning: SQL Instance name (-instanceName) not provided. Default of MSSQLSERVER will be used"
$instanceName = "MSSQLSERVER"
}
$tenantIdArgument = ""
if ([string]::IsNullOrEmpty($tenantId))
{
Write-Host "Warning: Tenant ID (-tenantId) not supplied to the script, so default tenant is being used"
}
else
{
$tenantIdArgument = "-TenantId '" + $tenantId + "'"
}
$subscriptionIdArgument = ""
if ([string]::IsNullOrEmpty($subscriptionId))
{
Write-Host "Warning: Subscription ID (-subscriptionId) not supplied to the script, so default subscription is being used"
}
else
{
$subscriptionIdArgument = "-SubscriptionId '" + $subscriptionId + "'"
}
# Login
#
try
{
$loginRes = Invoke-Expression -Command ("Connect-AzAccount " + $tenantIdArgument + " " + $subscriptionIdArgument + " -ErrorAction stop -UseDeviceAuthentication")
}
catch
{
Write-Error $_
Write-Error "Failed to login to Azure. Script can not continue"
exit 1
}
# Get subscription ID
#
if ([string]::IsNullOrEmpty($subscriptionId))
{
$context = Get-AzContext
if ($context)
{
if ($context.Name -Match "[^(]+\(([^)]{36})\)")
{
if ($Matches[1])
{
$subscriptionId = $Matches[1]
}
}
}
}
if ([string]::IsNullOrEmpty($subscriptionId))
{
Write-Error "Failed to find default subscription"
exit 1
}
# Check AKV path exists
#
$keyVault = Get-AzKeyVault -VaultName $keyVaultName
if (!$keyVault)
{
Write-Error "Supplied key vault was not found in the subscription. Please specify an existing key vault"
exit 1
}
# Check certificate doesn't exist
#
$cert = Get-AzKeyVaultCertificate -VaultName $keyVaultName -Name $certSubjectName
if ($cert)
{
Write-Error "Certificate $certSubjectName already exists"
exit 1
}
# Check app registration doesn't exist
#
$application = Get-AzADApplication -DisplayName $applicationName
if ($application)
{
Write-Error "Application $applicationName already exists"
exit 1
}
# Check Arc SQL instance is valid
#
$arcInstance = Get-AzConnectedMachineExtension -SubscriptionId $subscriptionId -MachineName $machineName -ResourceGroupName $resourceGroupName -Name "WindowsAgent.SqlServer"
if (!$arcInstance)
{
Write-Error "Could not find a SQL Server Arc instance in subscription '$subscriptionId' and resource group '$resourceGroupName' with name '$machineName'"
exit 1
}
# Check if admin account exists
#
$adminAccount = Get-AzADUser -UserPrincipalName $adminAccountName
$adminAccountType = 0
if (!$adminAccount)
{
# Check for guest user
#
$adminAccount = Get-AzADUser -Mail $adminAccountName
if (!$adminAccount)
{
$adminAccount = Get-AzADGroup -DisplayName $adminAccountName
if (!$adminAccount)
{
$adminAccount = Get-AzADServicePrincipal -DisplayName $adminAccountName
}
else
{
$adminAccountType = 1
}
}
}
if ($adminAccount)
{
if ($adminAccount.Length -gt 1)
{
Write-Error "Multiple accounts with found with name $adminAccountName"
exit 1
}
$adminAccountSid = $adminAccount.Id
}
else
{
Write-Error "Could not find an account with name $adminAccountName"
exit 1
}
# Create certificate in AKV
#
$Policy = New-AzKeyVaultCertificatePolicy -SecretContentType "application/x-pkcs12" -SubjectName "CN=$certSubjectName" -IssuerName "Self" -ValidityInMonths 12 -ReuseKeyOnRenewal
try
{
$addCertRes = Add-AzKeyVaultCertificate -VaultName $keyVaultName -Name $certSubjectName -CertificatePolicy $Policy -ErrorAction stop
}
catch
{
Write-Error $_
Write-Error "Certificate $certSubjectName could not be created"
exit 1
}
for (($i = 0); $i -lt $NUMRETRIES -and (!$cert -or !$cert.enabled); $i++)
{
$cert = Get-AzKeyVaultCertificate -VaultName $keyVaultName -Name $certSubjectName
if (!$cert -or !$cert.enabled)
{
Start-Sleep -Seconds 5
}
}
if (!$cert)
{
Write-Error "Certificate $certSubjectName could not be created"
exit 1
}
# Allow Arc to access AKV
#
$arcServicePrincipal = Get-AzADServicePrincipal -DisplayName $machineName
if ($arcServicePrincipal -and ![string]::IsNullOrEmpty($arcServicePrincipal.Id))
{
try
{
Set-AzKeyVaultAccessPolicy -VaultName $keyVaultName -ObjectId $arcServicePrincipal.Id -PermissionsToSecrets Get,List -PermissionsToCertificates Get,List
}
catch
{
Write-Error $_
Write-Host "Warning: Could not find the identity of the Azure extension for SQL Server and thus, could not add permissions for the Arc process to read from AKV. Ensure the Arc identity has the required permissions to read from AKV."
}
}
else
{
Write-Host "Warning: Could not find the identity of the Azure extension for SQL Server and thus, could not add permissions for the Arc process to read from AKV. Ensure the Arc identity has the required permissions to read from AKV."
}
# Create an Azure AD application
#
$application = New-AzADApplication -DisplayName $applicationName
if (!$application)
{
Write-Error "Application could not be created"
exit 1
}
# Set perms on app registration
#
Add-AzADAppPermission -ObjectId $application.Id -ApiId 00000003-0000-0000-c000-000000000000 -PermissionId c79f8feb-a9db-4090-85f9-90d820caa0eb # Delegated Application.Read.All
Add-AzADAppPermission -ObjectId $application.Id -ApiId 00000003-0000-0000-c000-000000000000 -PermissionId 0e263e50-5827-48a4-b97c-d940288653c7 # Delegated Directory.AccessAsUser.All
Add-AzADAppPermission -ObjectId $application.Id -ApiId 00000003-0000-0000-c000-000000000000 -PermissionId 7ab1d382-f21e-4acd-a863-ba3e13f7da61 -Type Role # Application Directory.Read.All
Add-AzADAppPermission -ObjectId $application.Id -ApiId 00000003-0000-0000-c000-000000000000 -PermissionId 5f8c59db-677d-491f-a6b8-5f174b11ec1d # Delegated Group.Read.All
Add-AzADAppPermission -ObjectId $application.Id -ApiId 00000003-0000-0000-c000-000000000000 -PermissionId a154be20-db9c-4678-8ab7-66f6cc099a59 # Delegated User.Read.All
# Upload cert to Azure AD
#
try
{
$base64Cert = [System.Convert]::ToBase64String($cert.Certificate.GetRawCertData())
New-AzADAppCredential -ApplicationObject $application -CertValue $base64Cert -EndDate $cert.Certificate.NotAfter -StartDate $cert.Certificate.NotBefore -ErrorAction stop
}
catch
{
Write-Error $_
Write-Error "Failed to add certificate to app registration"
exit 1
}
# Remove the version from the secret ID if present
#
$secretId = $cert.SecretId
if ($secretId -Match "(https:\/\/[^\/]+\/secrets\/[^\/]+)(\/.*){0,1}$") {
if ($Matches[1]) {
$secretId = $Matches[1]
}
}
# Create the settings object to write to the Azure extension for SQL Server
#
$instanceSettings = @{
instanceName = $instanceName
adminLoginName = $adminAccountName
adminLoginSid = $adminAccountSid
azureCertSecretId = $secretId.replace(":443", "")
azureCertUri = $cert.Id.replace(":443", "")
azureKeyVaultResourceUID = $keyVault.ResourceId
managedCertSetting = "CUSTOMER MANAGED CERT"
managedAppSetting = "CUSTOMER MANAGED APP"
appRegistrationName = $application.DisplayName
appRegistrationSid = $application.AppId
tenantId = $tenantId
aadCertSubjectName = $certSubjectName
adminLoginType = $adminAccountType
}
$arcInstance = Get-AzConnectedMachineExtension -SubscriptionId $subscriptionId -MachineName $machineName -ResourceGroupName $resourceGroupName -Name "WindowsAgent.SqlServer"
if ($arcInstance.Setting.AdditionalProperties.AzureAD)
{
$aadSettings = $arcInstance.Setting.AdditionalProperties.AzureAD
$instanceFound = $false
$instanceNameLower = $instanceName.ToLower()
$instanceIndex = 0
for (($i = 0); $i -lt $aadSettings.Length; $i++)
{
if ($aadSettings[$i].instanceName.ToLower() -eq $instanceNameLower)
{
$instanceIndex = $i
$instanceFound = $true
break
}
}
if ($instanceFound)
{
$aadSettings[$instanceIndex] = $instanceSettings
}
else
{
$aadSettings += $instanceSettings
}
$arcInstance.Setting.AdditionalProperties.AzureAD = $aadSettings
}
else
{
$aadSettings = , $instanceSettings
$arcInstance.Setting.AdditionalProperties | Add-Member -Name 'AzureAD' -Value $aadSettings -MemberType NoteProperty
}
Write-Host "Writing Microsoft Entra setting to SQL Server Arc Extension. This may take several minutes..."
# Push settings to Arc
#
try
{
Update-AzConnectedMachineExtension -MachineName $machineName -Name "WindowsAgent.SqlServer" -ResourceGroupName $resourceGroupName -Setting $arcInstance.Setting
}
catch
{
Write-Error $_
Write-Error "Failed to write settings to Arc host"
exit 1
}
Write-Output "Success"
通过 PowerShell 使用现有证书和应用程序设置 Microsoft Entra 管理员
如果已有现有的 Azure 密钥保管库证书与要用于设置 Microsoft Entra 管理员的 Azure 应用程序,可以使用以下 PowerShell 脚本:
# Connect statement
Connect-AzAccount
#Input parameters
$subscriptionId="<subscriptionId>"
$tenantId="<tenantId>"
$machineName="<machineName>" # hostname
$instanceName="<instanceName>" # SQL Server is define as `machine_name\instance_name`
$resourceGroupName="<resourceGroupName>"
$keyVaultName="<keyVaultName>"
$certSubjectName="<certSubjectName>" # Your existing certificate name
$applicationName="<applicationName>" # Your existing application name
$adminAccountName="<adminAccountName>"
$adminAccountSid="<adminID>" # Use object ID for the Microsoft Entra user and group, or client ID for the Microsoft Entra application
$adminAccountType= 0 # 0 – for Microsoft Entra user and application, 1 for Microsoft Entra group
$keyVault = Get-AzKeyVault -VaultName $keyVaultName
if (!$keyVault)
{
Write-Error "Supplied key vault was not found in the subscription. Please specify an existing key vault"
exit 1
}
$cert = Get-AzKeyVaultCertificate -VaultName $keyVaultName -Name $certSubjectName
if (!$cert)
{
Write-Error "Supplied certificate $certSubjectName was not found for this key vault. Please specify an existing certificate"
exit 1
}
$secretId = $cert.SecretId
if ($secretId -Match "(https:\/\/[^\/]+\/secrets\/[^\/]+)(\/.*){0,1}$") {
if ($Matches[1]) {
$secretId = $Matches[1]
}
}
$application = Get-AzADApplication -DisplayName $applicationName
if (!$application)
{
Write-Error "Supplied application was not found in the subscription. Please specify an existing application"
exit 1
}
# Create the settings object to write to the Arc extension
#
$instanceSettings = @{
instanceName = $instanceName
adminLoginName = $adminAccountName
adminLoginSid = $adminAccountSid
azureCertSecretId = $secretId.replace(":443", "")
azureCertUri = $cert.Id.replace(":443", "")
azureKeyVaultResourceUID = $keyVault.ResourceId
managedCertSetting = "CUSTOMER MANAGED CERT"
managedAppSetting = "CUSTOMER MANAGED APP"
appRegistrationName = $application.DisplayName
appRegistrationSid = $application.AppId
tenantId = $tenantId
aadCertSubjectName = $certSubjectName
adminLoginType = $adminAccountType
}
$arcInstance = Get-AzConnectedMachineExtension -SubscriptionId $subscriptionId -MachineName $machineName -ResourceGroupName $resourceGroupName -Name "WindowsAgent.SqlServer"
if ($arcInstance.Setting.AdditionalProperties.AzureAD)
{
$aadSettings = $arcInstance.Setting.AdditionalProperties.AzureAD
$instanceFound = $false
$instanceNameLower = $instanceName.ToLower()
$instanceIndex = 0
for (($i = 0); $i -lt $aadSettings.Length; $i++)
{
if ($aadSettings[$i].instanceName.ToLower() -eq $instanceNameLower)
{
$instanceIndex = $i
$instanceFound = $true
break
}
}
if ($instanceFound)
{
$aadSettings[$instanceIndex] = $instanceSettings
}
else
{
$aadSettings += $instanceSettings
}
$arcInstance.Setting.AdditionalProperties.AzureAD = $aadSettings
}
else
{
$aadSettings = , $instanceSettings
$arcInstance.Setting.AdditionalProperties | Add-Member -Name 'AzureAD' -Value $aadSettings -MemberType NoteProperty
}
Write-Host "Writing Microsoft Entra setting to SQL Server Arc Extension. This may take several minutes..."
# Push settings to Arc
#
try
{
Update-AzConnectedMachineExtension -MachineName $machineName -Name "WindowsAgent.SqlServer" -ResourceGroupName $resourceGroupName -Setting $arcInstance.Setting
}
catch
{
Write-Error $_
Write-Error "Failed to write settings to Arc host"
exit 1
}
Write-Output "Success"
以下 ARM 模板使用现有 Azure 密钥保管库证书和 Microsoft Entra 应用程序设置 Microsoft Entra 管理员。
以下输入参数用于 ARM 模板:
<machineName>
- SQL Server 主机的计算机名称<Location>
- SQL Server - Azure Arc 资源组的位置,例如West US
,或Central US
<tenantId>
– 可以通过转到 Azure 门户,然后转到 Microsoft Entra ID 资源找到租户 ID。 在“概述”窗格中,应会看到“租户 ID”<instanceName>
- SQL Server 实例名称。 SQL Server 的默认实例名称为MSSQLSERVER
<certSubjectName>
- 创建的证书名称<subscriptionId>
- 订阅 ID: 可以在 Azure 门户中找到订阅 ID<resourceGroupName>
- 转到包含密钥保管库的资源组。 要找到完整的 azureKeyVaultResourceUID 值,可转到密钥保管库资源,选择“属性”并复制资源 ID<keyVaultName>
- 密钥保管库名称<certIdentifier>
- Azure Key Vault 证书的证书标识符。 要获取证书标识符,请转到密钥保管库资源,然后在“设置”下选择“证书”。 选择已创建证书的当前版本,并复制证书标识符值。 有关详细信息,请参阅将证书添加到密钥保管库<certSecret>
- 证书的机密标识符,可以在证书 标识符所在的相同菜单中找到<applicationName>
– 已创建的 Microsoft Entra 应用程序的名称<appID>
– 可以在应用程序的“概述”菜单中找到 Microsoft Entra 应用程序的“应用程序(客户端)ID”<adminAccountName>
– 要为 SQL Server 设置的 Microsoft Entra 管理员帐户<adminID>
– 如果使用另一个应用程序作为 Microsoft Entra 管理员帐户,Microsoft Entra 用户或组的“对象 ID”或“应用程序(客户端)ID”。 有关更多信息,请参阅“教程:使用 Microsoft Entra 创建 Microsoft Entra 用户”。<adminType>
–0
用于 Microsoft Entra 用户和应用程序,1
用于 Microsoft Entra 组
使用 Azure 门户中的自定义部署,在编辑器中生成自己的模板。 接下来,粘贴到示例中后保存配置。
注意
对于 Linux 主机上的 SQL Server,请将脚本中的 WindowsAgent.SqlServer
替换为 LinuxAgent.SqlServer
。
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"resources": [
{
"type": "Microsoft.HybridCompute/machines/extensions",
"apiVersion": "2022-03-10",
"name": "<machineName>/WindowsAgent.SqlServer",
"location": "<Location>",
"properties": {
"publisher": "Microsoft.AzureData",
"type": "WindowsAgent.SqlServer",
"settings": {
"AzureAD": [
{
"tenantId": "<tenantId>",
"instanceName": "<instanceName>",
"managedCertSetting": "CUSTOMER MANAGED CERT",
"aadCertSubjectName": "<certSubjectName>",
"azureKeyVaultResourceUID": "/subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName>/providers/Microsoft.KeyVault/vaults/<keyVaultName>",
"azureCertUri": "<certIdentifier>",
"azureCertSecretId": "<certSecret>",
"managedAppSetting": "CUSTOMER MANAGED APP",
"appRegistrationName": "<applicationName>",
"appRegistrationSid": "<appID>",
"adminLoginName": "<adminAccountName>",
"adminLoginSid" : "<adminID>",
"adminLoginType": 0
}
]
}
}
}
]
}
向应用程序授予管理员同意
设置 Microsoft Entra 管理员后,使用 Microsoft Entra 管理员凭据即可连接到 SQL Server。 但是,在向 Microsoft Entra 应用程序授予管理员同意之前,涉及创建新的 Microsoft Entra 登录和用户的任何进一步数据库活动均将失败。
注意
要向应用程序授予“管理员同意”,授予同意的帐户需要 Microsoft Entra ID 全局管理员或特权角色管理员的角色。 这些角色是向应用程序授予管理员同意所必需的,但设置 Microsoft Entra 管理员则不需要。
在 Azure 门户,选择“Microsoft Entra ID”>“应用注册”,然后选择新创建的应用程序。 应用程序的名称应类似于
<hostname>-<instanceName><uniqueNumber>
。选择“API 权限”菜单。
选择“授予管理员同意”。
若未向应用程序授予管理员同意,在 SQL Server 中创建 Microsoft Entra 登录或用户将导致以下错误:
Msg 37455, Level 16, State 1, Line 2
Server identity does not have permissions to access MS Graph.
使用 Microsoft Entra 身份验证连接到 SQL Server
现已为连接到 Azure Arc 的 SQL Server 设置了 Microsoft Entra 身份验证。按照教程:为 SQL Server 设置 Microsoft Entra 身份验证一文中设置 Microsoft Entra 管理员之后的章节使用 Microsoft Entra 身份验证连接到 SQL Server。
另请参阅
反馈
此页面是否有帮助?