Principal 'xyz' could not be resolved: how can I add a managed identity to Azure SQL Server when running under a Service Principal? - sql-server

I want to add a managed identity (coming from an App Service) to Azure SQL Server.
I created an AAD group where a group of my team and the Service Principal is part of.
AzureSqlAdminGroup = TeamGroup + Service Principal
This AAD group is added as an Azure SQL admin during the provisioning of the Azure SQL Server.
When I run CreateSqlUserFromManagedIdentity under my personal account everything works fine. Whereas when I run the code under a service principal, SQL Server tells me that it can not resolve the managed identity of my app service and that the service principal doesn't have the permissions to do so.
System.Data.SqlClient.SqlException (0x80131904): Principal 'xyz' could not be resolved. Error message: ''
2020-06-10T16:34:12.6605990Z Cannot add the principal 'xyz', because it does not exist or you do not have permission.
2020-06-10T16:34:12.6606728Z Cannot add the principal 'xyz', because it does not exist or you do not have permission.
2020-06-10T16:34:12.6607420Z Cannot add the principal 'xyz', because it does not exist or you do not have permission.
Code:
public async Task CreateSqlUserFromManagedIdentity(string managedIdentityName, params string[] roles)
{
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
ExcludeVisualStudioCredential = true,
ExcludeVisualStudioCodeCredential = true
});
var accessToken = await credential.GetTokenAsync(new TokenRequestContext(new[] { "https://database.windows.net/.default" }));
var sqlConnectionStringBuilder = new SqlConnectionStringBuilder(_connectionString);
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"IF DATABASE_PRINCIPAL_ID('{managedIdentityName}') IS NULL");
stringBuilder.AppendLine("BEGIN");
stringBuilder.AppendLine($"\tCREATE USER [{managedIdentityName}] FROM EXTERNAL PROVIDER;");
stringBuilder.AppendLine("END");
Console.WriteLine($"Adding Managed Identity '{managedIdentityName}' to '{sqlConnectionStringBuilder.DataSource}\\{sqlConnectionStringBuilder.InitialCatalog}' with roles ...");
foreach (var role in roles)
{
Console.WriteLine($"\t{role}");
stringBuilder.AppendLine($"ALTER ROLE {role} ADD MEMBER [{managedIdentityName}];");
}
await using var sqlConnection = new SqlConnection(_connectionString) { AccessToken = accessToken.Token };
await sqlConnection.OpenAsync();
var sqlCommand = sqlConnection.CreateCommand();
sqlCommand.CommandText = stringBuilder.ToString();
await sqlCommand.ExecuteNonQueryAsync();
ConsoleEx.WriteSuccessLine("successfully");
}
How can I add a Managed Identity to Azure SQL Server when running under a Service Principal?
Clarification:
xyz is the Managed Identity I want to add as a user in Azure SQL. I am running the code under a service principal (which fails).

Related

Terraform - Azure Service Principal deployment - insufficient permissions

I am trying to create azure service principal. I am connection to azure from my laptop using service principal, I have added required permissions, service principal (which I am using to connect to azure) is a member of global administrators, sp is a member of an application developer and application administrator role in azure AD. In order to connect to Azure I am using following PowerShell commands.
$ApplicationId = "aaa"
$AppPassword = "bbb"
$TenantId = "ccc"
$SecuredPassword = ConvertTo-SecureString -String $AppPassword -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList
$ApplicationId, $SecuredPassword
Connect-AzAccount -ServicePrincipal -TenantId $TenantId -Credential $Credential
When I try run - terraform apply -var-file="variables.tfvars" I am getting an error:
with azuread_application.azuread_app,
│ on service-principal.tf line 3, in resource "azuread_application" "azuread_app":
│ 3: resource "azuread_application" "azuread_app" {
│
│ ApplicationsClient.BaseClient.Post(): unexpected status 403 with OData error: Authorization_RequestDenied: Insufficient privileges to complete the operation.
I was able to deploy other resources without any problems. I am using remote backend state file, located on Azure Container.
terraform code below:
data "azuread_client_config" "client_config" {}
resource "azuread_application" "azuread_app" {
display_name = "sp_name"
owners = [data.azuread_client_config.client_config.object_id]
}
resource "azuread_service_principal" "azuread_sp" {
application_id = azuread_application.azuread_app.application_id
app_role_assignment_required = false
owners = [data.azuread_client_config.client_config.object_id]
}
resource "azuread_service_principal_password" "azuread_sp_password" {
service_principal_id = azuread_service_principal.azuread_sp.object_id
}
I tested the same scenario in my environment using the below code and the Service Principal was successfully created from terraform.
terraform {
backend "azurerm" {
storage_account_name = "cloudshellansuman123" # replace with your storage account name
container_name = "test" #replace with your container name
key = "terraform.tfstate"
access_key = "ukyaH/Jxxxxxxxxxxxxxxxxx="#replace with your storage account access key
}
}
provider "azurerm" {
features{}
client_id="de398e56-xxxxxxxxxxxxxxxxxx-20d07416ecb0"#replace with your service principal client ID which you are using to connect with Azure
client_secret= "-IP7Q~uDLoxxxxxxxxxxxxRGtHMMXj7-.-lA"#replace with your service principal client Secret which you are using to connect with Azure
tenant_id = "ab07xxxxxxxxxxxxxxx--xxx-620b694ded30"#replace with your AzureAD tenant ID which the subscription is a part of
subscription_id="8xxxxxxxxxxx-xxxxxxx-xxxxxxxxx-xxae"#replace with your Subscription ID on Which the Service Principal has Owner/Contributor access
}
provider "azuread" {
client_id="de398e-xx-x-x-x-x-x-x-x416ecb0"#replace with your service principal client ID which you are using to connect with Azure AD
client_secret= "-IP7Q~uDLoxxxxxxxxxxxxxxGtHMMXj7-.-lA"#replace with your service principal client Secret which you are using to connect with Azure AD
tenant_id = "ab0xxxxxxxxxxxxxxx-xxxxxxxxx-xxxxxxxx30"#replace with your AzureAD tenant ID which the subscription is a part of
}
data "azuread_client_config" "current" {}
resource "azuread_application" "terraform" {
display_name = "Ansumantest"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_application_password" "terraform" {
application_object_id = azuread_application.terraform.object_id
}
resource "azuread_service_principal" "terraform" {
application_id = azuread_application.terraform.application_id
owners = [data.azuread_client_config.current.object_id]
}
For Testing I created a Service Principal which I am using to connect with Azure and granted all the same permissions that you have granted and also added a Owner access on the subscription like below :
Output:
Note: Using the above code , you don't need again to connect to Azure using PowerShell. It will get authenticated directly using the .tf configuration. Also make sure to use Latest AzureRM and AzureAD provider Versions in terraform i.e. 2.95.0 & 2.17.0 respectively.

Azure VM SQL Server connection string

*** Apologies folks - I appended wrong code ---now replaced below here
I have a simple Visual Studio .NET web forms app. I run it on my Azure VM called dexram (Windows 10) and I also created a SQL Server on the Azure VM. There is a user on the VM called 5001211 that has admin authority in Windows. It can use SSMS to access the database no problems.
All my connection string attempts fail in the C# code. This is strange as the VS web app and the SQL Server are both running on the Azure VM.
Here are the strings I tried and the messages I got underneath:
string Server = "Data Source = dexram; Initial Catalog = FruitNVeg; User ID=5001211;Password=Fitsh3ly;";
This connection string throws an error:
Login failed for user '5001211'
string Server = "Data Source = tcp:dexram,1433; Database = FruitNVeg; User ID = 5001211#dexram; Password = Fitsh3ly; Trusted_Connection = False; Encrypt = True;";
The certificate chain was issued by an authority that is not trusted
string Server = "Data Source = tcp:dexram,1433; Authentication = Active Directory Integrated; Database = FruitNVeg;";
The certificate chain was issued by an authority that is not trusted
string Server = "Data Source = tcp:dexram,1433; Authentication = Active Directory Password; Database = FruitNVeg; UID=5001211#dexram;PWD=Fitsh3ly;";
The certificate chain was issued by an authority that is not trusted
Thanks Dan - no luck - I created as per your suggestion and made 5001211 sysadmin and got following results:
string Server = "Data Source = dexram; Initial Catalog = FruitNVeg; User ID=5001211;Password=Fitsh3ly;";
Gives -- > Login failed for user '5001211'
string Server = "Data Source = tcp:dexram,1433; Database = FruitNVeg; User ID = 5001211#dexram; Password = Fitsh3ly; Trusted_Connection = True; Encrypt = True;";
Gives -- > The certificate chain was issued by an authority that is not trusted
I am thinking I need to get a cert. created as I think (?) my SQL calls from my VS app are going out over the internet (even though the 2 tools (VS and SQL Svr) are on the same VM machine) ?
you must first create a user in sql server after use from string format below
Data Source=instanse name or use .;Initial Catalog=database bame;User ID=created user in sql server;Password=your password
and do setting below for user
User dexram\5001211 is a Windows account. Your app connection string specifies a SQL login named 5001211. You need to create a SQL login named 5001211 and an associated database user:
USE FruitNVeg;
CREATE LOGIN [5001211] WITH PASSWORD = 'Fitsh3ly';
CREATE USER [5001211];
The user will also need permissions on the objects the application uses in the FruitNVeg database. Although you could add the login to a privileged role like sysadmin to avoid granting these permissions, the best practice is to use a minimally privileged account for routine application database access that has only the required permissions:
USE FruitNVeg;
GRANT SELECT ON dbo.Apples TO [5001211];
As per this URL --> https://blog.greglow.com/2020/01/16/sql-sql-server-the-certificate-chain-was-issued-by-an-authority-that-is-not-trusted/
I used the sql config manager and set "Trust Server Cert" to yes and that fixed the problem it seems

Add azure SQL Server login using terraform

Is it possible to add an user as active directory admin for an azure sql server using terraform?
https://learn.microsoft.com/pt-br/azure/sql-database/sql-database-aad-authentication
I need this to be enable users to authenticate through their company logins to a sql server created using Terraform.
I've found this question:
Add azure SQL user with terraform
But it is not what I need, it creates a new user for a login. Terraform docs regarding azure do not document this action.
https://www.terraform.io/docs/providers/azurerm/r/sql_server.html
Please reference this link: Active Directory Admin for azurerm_sql_server:
Support for configuring Azure Active Directory Administrators for a SQL Server Database can be found in the azurerm_sql_active_directory_administrator resource.
azurerm_sql_active_directory_administrator:
Allows you to set a user or group as the AD administrator for an Azure SQL server.
Example useage:
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "example" {
name = "acceptanceTestResourceGroup1"
location = "West US"
}
resource "azurerm_sql_server" "example" {
name = "mysqlserver"
resource_group_name = "${azurerm_resource_group.example.name}"
location = "${azurerm_resource_group.example.location}"
version = "12.0"
administrator_login = "4dm1n157r470r"
administrator_login_password = "4-v3ry-53cr37-p455w0rd"
}
resource "azurerm_sql_active_directory_administrator" "example" {
server_name = "${azurerm_sql_server.example.name}"
resource_group_name = "${azurerm_resource_group.example.name}"
login = "sqladmin"
tenant_id = "${data.azurerm_client_config.current.tenant_id}"
object_id = "${data.azurerm_client_config.current.object_id}"
}
Hope this helps.

Azure AD B2C and SQL Server

I have Azure B2C setup using OpenID Connect. I also have a local SQL Server database. I know that the id_token (Jtw token) can contain the logged-in user's email, so my question is how do I get the user's email from the id_token and use it to run queries?
From within an ASP.NET Core application, you can do the following:
var user = HttpContext.User;
var emailsClaim = user.FindFirst("emails");
var emails = JArray.Parse(emailsClaim);
var firstEmail = emails[0].value;
//firstEmail == “bob#gmail.com” based up the sample token
Sample Token

Setting up custom principal to impersonate Sql Server user

What do I need to do in order to use custom principal (Preferrably a claim based one) to impersonate a Sql Server user.
var claims = new List<Claim>
{
/* What do I need in here? */
};
var claimsIdentity = new ClaimsIdentity(
claims, "Basic", "UserName", ClaimTypes.Role);
Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity);
In the end I want SQL Server to trust my application layer, and the principal/role that it defines.
SQL Server does not support that.

Resources