0
votes

I have a Powershell Runbook where I am trying to add AzureAD user as Database owner on an Azure SQL database.

## Connect
$servicePrincipalConnection = Get-AutomationConnection -Name "AzureRunAsConnection"
Connect-AzureAD `
    -TenantId $servicePrincipalConnection.TenantId `
    -ApplicationId $servicePrincipalConnection.ApplicationId `
    -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 

## Generate new access token
$cert = Get-AutomationCertificate -Name 'AzureRunAsCertificate'
# Set Resource URI to Azure Database
$resourceAppIdURI = 'https://database.windows.net/'
# Set Authority to Azure AD Tenant
$authority = 'https://login.windows.net/' + $servicePrincipalConnection.TenantId
$ClientCred = [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate]::new($servicePrincipalConnection.ApplicationId, $cert)
$authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($authority)
$authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $ClientCred)
$AccessToken = $authResult.Result.AccessToken

## Execute sql
$AccessToken
$connectionString = "Data Source=MYCOOLSQLSERVER.database.windows.net;Initial Catalog=MYCOOLDATABASE;Connect Timeout=30"
$connection = New-Object -TypeName System.Data.SqlClient.SqlConnection($connectionString)
$query = "Create User [[email protected]] From EXTERNAL PROVIDER;"

$command = New-Object -TypeName System.Data.SqlClient.SqlCommand($query, $connection)
$connection.AccessToken = $AccessToken
$connection.Open()
$command.ExecuteNonQuery()
$connection.Close()

I end up getting the error below where [email protected] is an AzureAD user.

Principal '[email protected]' could not be resolved.

Is there something that I have missed out?

Once the user gets created, I intend to use Alter role to make him the db_owner.

References: https://blogs.technet.microsoft.com/stefan_stranger/2018/06/06/connect-to-azure-sql-database-by-obtaining-a-token-from-azure-active-directory-aad/

https://techcommunity.microsoft.com/t5/Azure-Database-Support-Blog/Azure-SQL-Database-Token-based-authentication-with-PowerShell/ba-p/369078

2
Could you please tell me if you can run the command "Create User [[email protected]] From EXTERNAL PROVIDER;" in SSMS?Jim Xu
Yes, it works through SSMS. But in SSMS I connect using my own AzureAD user whereas the script above uses SPN of the Automation accountbit

2 Answers

1
votes

Turns out that there is an undocumented way to do this. Discovered it with the help of Azure support team.

The SQL query to be used is actually:

CREATE USER [[email protected]] WITH SID = $hexSid, Type E;

Here $hexSid can be obtained by the following SQL query

  1. Get the ObjectId of the AzureAD User

$objectId = (Get-AzureADUser -ObjectId "[email protected]").ObjectId

  1. Use the SQL below to get the sid

DECLARE @sid = uniqueidentifier = cast('$objectId' as uniqueidentifier) select cast(@sid as varbinary(max))

  1. Now ideally, the effective query would have directly used the @sid but that is not possible since the With SID needs a hard coded value. So I had to process the output of the sid query separately like below:
$result = $command.ExecuteScalar() # result is byte array
$hexSid = ($result | ForEach-Object ToString X2) -Join ''  # convert to hex format
$hexSid = "0x$hexSid"

With those changes the code in the question just works fine!

Effectively, we are only passing the AzureAD User's objectId in the Create User query.

0
votes

for this scenario, we should use user password flow to make this command work. Pls try the PS below :

$tenant='<your tenant name/id>'
$username='<SQL admin account>'
$password='<SQL admin password>'
$appid='<app id>'
$appsec='<app secret>'

$SQLServerName = '<azure sql servername>'
$DatabaseName='<db name>'

$body=@{
    "grant_type"="password";
    "resource"="https://database.windows.net/";
    "client_id"=$appid;
    "client_secret"=$appsec;
    "username"=$username;
    "password" = $password;
}
 
$result=Invoke-RestMethod -Uri "https://login.windows.net/$tenant/oauth2/token" -Method POST -Body $body 



$conn = New-Object System.Data.SqlClient.SQLConnection 
$conn.ConnectionString = "Data Source=$SQLServerName.database.windows.net;Initial Catalog=$DatabaseName;Connect Timeout=30"
$conn.AccessToken = $result.access_token

Write-Verbose "Connect to database and execute SQL script"
$conn.Open() 

$query = 'CREATE USER [Account] FROM EXTERNAL PROVIDER;'

$command = New-Object -TypeName System.Data.SqlClient.SqlCommand($query, $conn)     
$Result = $command.ExecuteScalar()
$Result
$conn.Close() 

Before you run is script, you should grant user_impersonation Azure SQL API access permission for your SPN in Azure AD here : enter image description here enter image description here Grant permission after added : enter image description here

Btw, pls makre sure that the SQL admin account and SPN are all members of Active Directory admin group : enter image description here

It works on my side and a record has been added successfully : enter image description here

If there is anything unclear , pls feel free to let me know : )