Connect to Azure SQL Database using Managed Identity - azure

I am trying to configure connecting to azure sql using managed identity. However before, I need to connect to sql azure from visual studio using AD identity.
I have followed steps here.
https://learn.microsoft.com/en-us/azure/app-service/tutorial-connect-msi-sql-database?tabs=windowsclient%2Cef%2Cdotnet#3-modify-your-project
I have this code in console app.
SqlConnectionStringBuilder builder = new
Microsoft.Data.SqlClient.SqlConnectionStringBuilder();
builder.DataSource = "sqlserveraddress";
builder.InitialCatalog = "dbaddress";
string connstring = builder.ConnectionString;
await using var conn = new Microsoft.Data.SqlClient.SqlConnection(connstring)
{
AccessToken = await GetAzureSqlAccessToken()
} ;
await conn.OpenAsync();
As required, I have added my visual studio App service authentication identity user in sql azure db.
CREATE USER "user#domain.com" FROM EXTERNAL
PROVIDER;
ALTER ROLE db_datareader ADD MEMBER
"user#domain.com";
ALTER ROLE db_datawriter ADD MEMBER
"user#domain.com";
ALTER ROLE db_ddladmin ADD MEMBER
"user#domain.com";
GO
Now if i try running my console application, I get following error.
If i try to decode the access token it shows the user I have added to sql azure. user#domain.com.
What Am I Missing here??

You need to set the created Managed Identity as admin in SQL Server.
Follow this: Azure SQL Server -> Settings -> Azure Active Directory
Now click on Set Admin option and search for the Managed Identity to which you want to give access.
Click on Save.

Related

Microsoft Graph permissions issue when using managed identity and DefaultAzureCredential

I have set up a test project that follows this microsoft guide: https://learn.microsoft.com/en-us/azure/app-service/scenario-secure-app-access-microsoft-graph-as-app?tabs=azure-powershell
The only difference that I made from that tutorial is the code portion. I changed it to look like this:
TokenCredential tokenCredential = new DefaultAzureCredential();
var scopes = new[] { "https://graph.microsoft.com/.default" };
var graphClient = new GraphServiceClient(tokenCredential, scopes);
var group = graphClient.Groups["<my-group-id>"].Request().GetAsync().Result;
Everything works as expected when I publish the website and access it, but when I run this code locally I receive
Insufficient privileges to complete the operation.
I am signed into VS using the same account that I am using in Azure portal (it's a global admin account). Is there any other configuration setting that I am missing so that I can run this code and test locally?
Usually you need one of the following permissions to query groups i.e delegated and application permissions : GroupMember.Read.All, Group.Read.All, Directory.Read.All, Group.ReadWrite.All, Directory.ReadWrite.All User.Read.All
Run VS as administrator and also give user administrator role.
But visual studio may not work in this case . So please try with
different credential type like client secret/certificate credential
with your app .
In local debugging ,use Shared Token Cache Credential ,as in
your local environment, DefaultAzureCredential uses the shared token
credential from the IDE.
In Visual Studio, you can set the account that you want to use when
debugging using VS : under Options -> Azure Service Authentication.
Please check Azure Managed Service Identity And Local Development by Rahul Nath (rahulpnath.com)
If multiple accounts are configured, try to set the SharedTokenCacheUsername property to that specific account to use.
var azureCredentialOptions = new DefaultAzureCredentialOptions();
azureCredentialOptions.SharedTokenCacheUsername = "<azure ad User Name>";
var credential = new DefaultAzureCredential(azureCredentialOptions);
Reference: DefaultAzureCredential: Unifying How We Get Azure AD Token | Rahul Nath (rahulpnath.com)

.net core key vault link fails with Access Forbidden on local environment

I am running the following functionality as a part of Main method of my .net core web application
private static void LinkKeyVault(IConfigurationBuilder config, string keyVaultEndpoint)
{
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
config.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
}
}
When I am running this code on my local dev machine I am getting the following error "Operation returned an invalid status code 'Forbidden'". When this code runs in Azure under app service user assigned managed identity everything works just fine. On my local environment I am logged in with my Azure AD user which was granted access permissions using key vault access policy, the permissions are the same as for user assigned managed identity.
dfrds-dev-web-identity is the user assigned managed identity, DFRDDevelopers is a group that my Azure AD account is a part of.
It should work, please make sure the group in which the user account located is a Security group, not a Microsoft 365 group, just the Security group is supported in this feature.
Reference - https://learn.microsoft.com/en-us/azure/key-vault/general/secure-your-key-vault#data-plane-and-access-policies
To grant data plane access to several users, create an Azure AD security group and add users to that group.
Also, if you want to use Visual Studio to auth, make sure you logged in with the correct account, and try to use RunAs=Developer; DeveloperTool=VisualStudio in the code to make sure it uses the Visual Studio to auth.
var azureServiceTokenProvider = new AzureServiceTokenProvider(RunAs=Developer; DeveloperTool=VisualStudio);

Azure function - connect to Azure SQL database using Active Directory Interactive authentication

I'm using SqlClient in my Azure function. Currently when I try to create a new instance passing the connection string, I receive the following message
Keyword not supported: 'authentication'
My connection string is
server=tcp:mydbserver.database.windows.net;database=mydb;UID=AnyString;Authentication=Active Directory Interactive
My azure function has 'Identity' setting enabled.
My other .NET apps running as AppService are working excellent connecting to the same DB, but they use EntityFramework
I have tried to remove Authentication=Active Directory Interactive and also add the following line to the connection
connection.AccessToken = new Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").Result;
But I'm just getting different error messages like Login failed for user '' or Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'
Thanks!
Make sure you use Microsoft.Data.SqlClient not System.Data.SqlClient namespace
I got success with the following steps:
1. Provision an Azure Active Directory administrator for your Azure SQL Database server.
Note: Please ensure that you set a user as the AAD administrator for your Azure SQL Server.
2. Connect to your Azure SQL Database with the AAD account in step 1. You can use SSMS or just connect to your database from the portal.
3. Add service principal and assign a role
CREATE USER [Azure_AD_principal_name] FROM EXTERNAL PROVIDER;
EXEC sp_addrolemember 'db_datareader', 'Azure_AD_principal_name';
4. Code sample
string sqlResource = "https://database.windows.net/";
string tenantId = "e4c9ab4e-****-****-****-230ba2a757fb";
string serverName = "{server}.database.windows.net";
string databaseName = "{database}";
var token = new AzureServiceTokenProvider().GetAccessTokenAsync(sqlResource, tenantId).ConfigureAwait(false).GetAwaiter().GetResult();
string sqlConnectionString = String.Format("Data Source=tcp:{0},1433;Initial Catalog={1};Persist Security Info=False;Connect Timeout=30;Encrypt=True;TrustServerCertificate=False", serverName, databaseName);
using (SqlConnection connection = new SqlConnection(sqlConnectionString))
{
connection.AccessToken = token;
using (SqlCommand command = new SqlCommand("SELECT Distinct TABLE_NAME FROM information_schema.TABLES", connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine(reader[0]);
}
}
}
}
I believe the connection.AccessToken property is the only way of doing this in .NET Core as per this conversation.
As for the error you are seeing with this, is probably because a user for the managed identity has not been created in the database. Follow the steps in this section for that.
Here is the SQL Script for reference
CREATE USER [<identity-name>] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [<identity-name>];
ALTER ROLE db_datawriter ADD MEMBER [<identity-name>];
ALTER ROLE db_ddladmin ADD MEMBER [<identity-name>];
GO
where <identity-name> is the name of the managed identity configured in Azure AD.

VS2019 Azure Service Authentication Account Selection for local debugging

Using VS2019, I have specified a temporary "developer account", using Tools/Options/Azure Service Authentication/Account Selection, for my app to "authenticate and access Azure resources with when debugging from Visual Studio". The developer account has access to an Azure SQL database. When I debug, my app gets a token as follows:
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder["Data Source"] = serverName;
builder["Initial Catalog"] = dbName;
builder["Persist Security Info"] = "False";
builder["MultipleActiveResultSets"] = "False";
builder["Encrypt"] = "True";
builder["TrustServerCertificate"] = "False";
builder["Connect Timeout"] = 15;
var cancellationToken = new CancellationToken();
var conn = new SqlConnection(builder.ConnectionString);
conn.AccessToken = new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").GetAwaiter().GetResult();
return conn.AccessToken;
However, when I examine the token returned it is NOT the token for the developer account identity I specified, it is for my normal identity I use to run Visual Studio. A co-worker has been able to do this in VS2019 with a developer account that's in the same Azure AD groups as my developer account. But for some local environment reason this is not working for me. Note that in SQL Server Management Studio I can access the SQL Azure database using my developer account with no problem at all.
Has anyone else had this debugging identity problem and been able to solve it? Thanks in advance for any light you can shed on this.
Sounds like you need to add the developer account to your Azure Service Authentication
On the drop down add the Dev account you are trying to use and make sure you make it your default account for Azure.

Can't Create Azure SQL Database Users Mapped to Azure AD Identities using Service Principal

As part of an Azure SQL database automation solution, I'm trying to create Azure SQL database users mapped to Azure AD Identities, using a service principal.
The result is an error message saying: Principal 'AAD_User_UPN_or_Group_Name' could not be found at this time. Please try again later.
The database users can be created using my own user account, following exactly the same procedure.
Please find more details below:
The service principal is a member of an Azure AD security group
The group is set as the Active Directory Admin of an Azure SQL server
My own user account is also a member of this group
The service principal has Directory Reader and Directory Writer role in the Azure Active Directory
My own user account is a regular member without any admin role in the Azure Active Directory
The service principal executes following T-SQL statement inside the Azure SQL database:
CREATE USER [AAD_User_UPN_or_Group_Name] FROM EXTERNAL PROVIDER;
The returned error message is:
Principal 'AAD_User_UPN_or_Group_Name' could not be found at this time. Please try again later.
When the same T-SQL statement is triggered by my own user account, it runs successfully and the user is created.
Your help or suggestions are highly appreciated.
I opened a ticket with Azure support and they gave me this solution.
The sql statement needs to be:
-- type X for AAD Group
create user [myAADGroupName] with sid = <sid>, type = X;
-- type E for AAD User or Service Principal/MSI
create user [myAADUserName] with sid = <sid>, type = E;
The sid needs to be generated from the AAD Principal ObjectID in most cases. However, for Service Principals/MSIs, it needs to come from the AppId. Here's a powershell script to generate the sid value:
param (
[string]$objectIdOrAppId
)
[guid]$guid = [System.Guid]::Parse($objectIdOrAppId)
foreach ($byte in $guid.ToByteArray())
{
$byteGuid += [System.String]::Format("{0:X2}", $byte)
}
return "0x" + $byteGuid

Resources