Accessing Azure SQL Connection String from Azure Key Vault in ASP.NET Core API
In this article, we will see the use of Azure Key Vault access in ASP.NET Core application. Azure Key Vault is a cloud service for securely storing and accessing secrets. A secret is anything that we want to tightly control access to, such as Database Connection Strings, API Auth keys, passwords, certificates, cryptographic keys, etc. The Azure Key Vault helps to solve problems as follows:
- Secrets Management - It can be used to Securely store and tightly control access to tokens, passwords, certificates, API keys, and other secrets like Database Connection String
- Key Management - It can be used as a Key Management solution. Azure Key Vault makes it easy to create and control the encryption keys used to encrypt the data of the Application.
- Certificate Management - It allows an easy provision, manage, and deploy public and private Transport Layer Security/Secure Sockets Layer (TLS/SSL) certificates for use with Azure.
More information of Azure Key Vault can be read from this link.
This article is divided into two sections, in the first section we will see the creation of Azure Key Vault and adding secret in it. In the second section we will see the access of the Azure Key Vault in ASP.NET Core Application.
Creating Azure Key Vault and adding secret in it
Step 1: To create Azure Key Vault, we need the Azure Subscription. You can get the same from this link. Login using your subscription. Once you login, go to Microsoft Entra ID and from the Properties option, enable the Access management for Azure resources as shown in Figure 1
Figure 1: The Access Management for the Subscription
This is the important step for the implementation. Why? The reason is that although we can the Azure Key Vault, we need to provide its access to the User and the Application so that Secret from the Azure Key Vault can be accessed.
Step 2: Create a resource group from the resources by filling the required information as shown in Figure 2.
Figure 2: Creating a resource group
We will create the Azure Key Vault in this resource group.
Step 3: Create a Key Vault as shown in Figure 3
Figure 3: Creating a Kay Vault
While creating a Key Vault, we must think about the Soft-delete behavior. This behavior provides a mechanism for recovering the deleted object, essentially undoing the deletion. The default time period is 90-days. Once soft-delete is enabled on a key vault it can't be disabled. The retention policy interval can only be configured during key vault creation and can't be changed afterwards. We can change this from 7 to 90 days.
Purge protection is an optional Key Vault behavior. This is not enabled by default. Purge protection can only be enabled once soft-delete is enabled. This is recommended when using keys for encryption to prevent data loss. All those Azure services that integrate with Azure Key Vault, e.g. Storage, require purge protection to prevent data loss. When purge protection is on, a vault or an object in the deleted state can't be purged until the retention period has passed.
Click on Next button, the Access Configuration page will be displayed, on this page we need to define Permissions to access the Azure Key Vault by users as well as by applications. The default permission is Azure Role-Based Access Control (RBAC). On the Networking page select the Enable Public access and provide access from All Networks.
Click on Review and Create button to create a Key Vault.
Step 4: Once the Key Vault is created, we need to store secrets in it. In this case, we will be storing Azure SQL Connection String in it. Create a database in Azure SQL and Copy its Connection String. Click on Secrets on its Property pan as shown in Figure 4
Figure 4: The Secrets
To generate new secret, click on the Generate/Import button, this will open the Create a secret page where we can need to enter Upload Options, Name, Secret Value, etc. Figure 5 shows the secret details those can be stored in Key Vault. Make sure that the Name is unique. Set the Azure SQL Connection String as a Secret vale as shown in Figure 5.
Figure 5: Create a secret
Click on the Create button.
Once the Secret is creates, we need to make sure that the role assignment is configured to access the Azure Key Vault. Click on the Access control (IAM) and click on Add role assignment button as shown in Figure 6
Figure 6: Access control to add role assignment
Once the Add role assignment button is clicked, on the next page we can grant access to Azure resources based on the job function e.g. Key Vault Administrator, Key Vault Reader, Key Value Secret Officer as shown in Figure 7
Figure 7: The Job function role
Once roles for Azure Key Vault are selected, click on member and select the member who can use the Key Vault role as shown in Figure 8, on this page we need to click on Select members link to select Group or applications to those we want to assign role.
Figure 8: Members to assign role
On this page we will be selecting applicationaccess group. This group will make sure that the application running on the development machine will be able to access Key Vault based on roles assignment, click on the Select button to save the member and then click on the Review and Create button.
Once the Access Control is set, we need to provide the Access Permission to our application so that it can access secret from the Azure Key Vault. Click on the Access configuration of the Key Vault and select the Value access policy as shown in Figure 9
Figure 9: Access Configuration
By selecting Value access policy, we can grant access permission to the application or user group to perform different operations on Secret. This is the most important step; this step will make sure that the Azure Key Vault is accessible to our application. Click on the Go to access policies, it will open the Access Policies page where we can assign policies to access the Secret from the application like Get, List, etc. Figure 10, shows the Access Policies Page as shown in Figure 10
- Azure.Security.KeyVault.Secrets
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Relational
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tool
- Microsoft.EntityFrameworkCore.Design
"KeyVault": { "VaultUri": "https://wsit0031133333333.vault.azure.net/" }
dotnet ef dbcontext scaffold "Server=tcp:myserver.database.windows.net,1433;
Initial Catalog=eShoppingCodi;Persist Security Info=False;User ID=MyAdmin;
Password={your_password};MultipleActiveResultSets=False;Encrypt=True;
TrustServerCertificate=False;Connection Timeout=30;"
Microsoft.EntityFrameworkCore.SqlServer -o Models
public interface IKeyVaultStoreManager { public Task<string> GetVaultSecret(string secretName); }
using Azure.Security.KeyVault.Secrets; namespace API_Azure_KeyValue.Repositories { public class KeyVaultStoreManager : IKeyVaultStoreManager { private readonly SecretClient _secretClient; public KeyVaultStoreManager(SecretClient secretClient) { _secretClient = secretClient; } public async Task<string> GetVaultSecret(string secretName) { try { KeyVaultSecret keyValueSecret = await _secretClient.GetSecretAsync(secretName); return keyValueSecret.Value; } catch (Exception ex) { throw ex; } } } }
using API_Azure_KeyValue.Models; using Microsoft.EntityFrameworkCore; namespace API_Azure_KeyValue.Repositories { public class ProductInfoRepo { IKeyVaultStoreManager mgr; EShoppingCodiContext ctx; public ProductInfoRepo(IKeyVaultStoreManager mgr, EShoppingCodiContext ctx) { this.mgr = mgr; this.ctx = ctx; } public async Task<List<ProductInfo>> GetProducts() { return await ctx.ProductInfos.ToListAsync(); } } }
//1. Register AzureFactoryClientBuilder class, to read // section from appsettings.json so that we will be able to // create a registeration for Azure SDK clients. builder.Services.AddAzureClients(azureClientFactoryBuilder => { azureClientFactoryBuilder.AddSecretClient(builder.Configuration.GetSection("KeyVault")); }); //2. Register IKeyVaultStoreManager, KeyVaultStoreManager builder.Services.AddSingleton<IKeyVaultStoreManager, KeyVaultStoreManager>(); builder.Services.AddDbContext<EShoppingCodiContext>(options => { // Read the ConnectionString using the GetVaultSecret() method of IKeyVaultStoreManager IServiceProvider serviceProvider = builder.Services.BuildServiceProvider(); var service = serviceProvider.GetService<IKeyVaultStoreManager>(); var connectionString = service.GetVaultSecret("connstr").Result; options.UseSqlServer(connectionString); }); builder.Services.AddTransient<ProductInfoRepo>();
..... app.MapGet("/api/products", async (ProductInfoRepo repo) => { return await repo.GetProducts(); }); ......