Skip to content

Instantly share code, notes, and snippets.

@snobu
Created December 13, 2021 12:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snobu/684b768fc2f00654b74f6d5fe525d49c to your computer and use it in GitHub Desktop.
Save snobu/684b768fc2f00654b74f6d5fe525d49c to your computer and use it in GitHub Desktop.
Use Managed Identity to access Azure SQL
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Azure.Core" version="1.20.0" targetFramework="net472" />
<package id="Azure.Identity" version="1.5.0" targetFramework="net472" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.0.0" targetFramework="net472" />
<package id="Microsoft.Identity.Client" version="4.30.1" targetFramework="net472" />
<package id="Microsoft.Identity.Client.Extensions.Msal" version="2.18.4" targetFramework="net472" />
<package id="System.Buffers" version="4.5.1" targetFramework="net472" />
<package id="System.Diagnostics.DiagnosticSource" version="4.6.0" targetFramework="net472" />
<package id="System.Memory" version="4.5.4" targetFramework="net472" />
<package id="System.Memory.Data" version="1.0.2" targetFramework="net472" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.6.0" targetFramework="net472" />
<package id="System.Security.Cryptography.ProtectedData" version="4.5.0" targetFramework="net472" />
<package id="System.Text.Encodings.Web" version="4.7.2" targetFramework="net472" />
<package id="System.Text.Json" version="4.6.0" targetFramework="net472" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net472" />
</packages>
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Azure.Identity;
using Azure.Core;
namespace NET47ManagedIdentitytoAzureSQL
{
class MainClass
{
public static async Task Main(string[] args)
{
// You should be reading these from your app configuration.
// Treat both server and database name as secrets (e.g. read them from Key Vault)
string server = "<YOUR_AZURE_SQL_INSTANCE>.database.windows.net";
string database = "<DATABASE_NAME>";
string connectionString = $"Data Source={server}; Initial Catalog={database}; Encrypt=True; Trusted_Connection=False;";
SqlConnection conn = new SqlConnection(connectionString);
// Before you can use access tokens you need to enable Managed Identity
// (either system or user assigned) on your Virtual Machine(s) -
// https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/tutorial-windows-vm-access-sql#enable
//
// Then, on the Azure SQL side create a contained user and assign roles to it -
// https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/tutorial-windows-vm-access-sql#create-contained-user
//
// In essence:
// CREATE USER [VM_NAME] FROM EXTERNAL PROVIDER
// ALTER ROLE db_datareader ADD MEMBER [VM_NAME]
//
// VM_NAME is the name of the *system* assigned Managed Identity for your VM.
// It's the same with the VM name by default.
//
// If you use *user* assigned managed identity use the string literal name
// of the identity as your [VM_NAME] value
// Let's use *user* assigned managed identity -
string userAssignedClientId = "b1b8ea61-32a6-466c-8c81-1102545fc1e0";
string audience = "https://database.windows.net";
// Open a connection to the database using an access token
try
{
AccessToken accessToken = await GetManagedIdentityAccessToken(audience, userAssignedClientId);
conn.AccessToken = accessToken.Token;
conn.Open();
}
catch (Exception ex)
{
Console.WriteLine($"[EXCEPTION] {ex.Source}: {ex.Message}");
Console.ReadKey();
Environment.Exit(11);
}
Console.WriteLine($"Connection state is {conn.State} to database {conn.Database}\n");
Console.WriteLine("List of tables in the database:");
string query = @"SELECT * FROM INFORMATION_SCHEMA.TABLES";
SqlCommand cmd = new SqlCommand(query, conn);
SqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
// Show retrieved records
Console.WriteLine($"{dr.GetString(0)} " +
$"{dr.GetString(1)} " +
$"{dr.GetString(2)} " +
$"{dr.GetString(3)}");
}
}
else
{
Console.WriteLine("Connection to the database is successful but the query returned no data.");
}
Console.WriteLine("\nPress any key to exit.");
Console.ReadKey();
}
/// <summary>
/// Get a Managed Identity access token with a *system* assigned identity
/// </summary>
/// <param name="audience">The resource token gets issued for</param>
/// <returns></returns>
public static async Task<AccessToken> GetManagedIdentityAccessToken(string audience)
{
// For *system* assigned managed identity -
var credential = new DefaultAzureCredential();
TokenRequestContext context = new TokenRequestContext(
// The value here is also known as "resource" or token "audience"
new[] { audience });
AccessToken accessToken = await credential.GetTokenAsync(context);
return accessToken;
}
/// <summary>
/// Get a Managed Identity access token with a *user* assigned identity
/// </summary>
/// <param name="audience">The resource token gets issued for</param>
/// <param name="userAssignedClientId">Client ID (GUID) of user assigned identity</param>
/// <returns></returns>
public static async Task<AccessToken> GetManagedIdentityAccessToken(string audience, string userAssignedClientId)
{
// For *user* assigned managed identity -
// Use the GUID of the user assigned managed identity when creating
// the DefaultAzureCredential instance.
var credential = new DefaultAzureCredential(
new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId });
TokenRequestContext context = new TokenRequestContext(
// The value here is also known as "resource" or token "audience"
new[] { audience });
AccessToken accessToken = await credential.GetTokenAsync(context);
return accessToken;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment