2

I've implemented a TimeTriggerC# Azure Function to Run a Store Procedure to an Insert a table that is Encrypted using Column Encryption(Always Encrypted, AzureKey Vault As Provider)

The Blocking area for me is that my Function ran successfully at 1 time and got an error for again many times. So I get the success path as a rare case

The Error I faced is

mscorlib: Exception has been thrown by the target of an invocation. System.Data: Key store providers cannot be set more than once.

I done some investigation on this It occurs in the line

SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);

With in the function

static void InitializeAzureKeyVaultProvider()
{
    string clientId = ConfigurationManager.AppSettings["AuthClientId"];
    string clientSecret = ConfigurationManager.AppSettings["AuthClientSecret"];
    _clientCredential = new ClientCredential(clientId, clientSecret);

    // Direct the provider to the authentication delegate
    SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(GetToken);

    Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
    providers.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider);

    // register the provider with ADO.net
    SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);
}

When I tried to look much deeper into the root cause of the error, it will be like

Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException : Key store providers cannot be set more than once. at System.Data.SqlClient.SqlConnection.RegisterColumnEncryptionKeyStoreProviders(IDictionary`2 customProviders)

As I mentioned above I used Azure Key Vault as my Key Store Provider.I properly registered my Keyvault with AD and added the registered application to my key vault (Access Policies).

Why I'm facing this abnormal behavior, like one-time success and another time failure?

Appreciate your Response,
Jayendran

juanferrer
  • 1,192
  • 1
  • 16
  • 29
Jayendran
  • 9,638
  • 8
  • 60
  • 103
  • Where are you calling your `InitializeAzureKeyVaultProvider()` method? It looks from the error message that you do that more than once. – Max Jul 28 '17 at 08:24

2 Answers2

4

You are probably running InitializeAzureKeyVaultProvider() inside every function call. If works for the first time, when initialization wasn't performed on this App instance.

During the next function call the instance gets reused, so the initialization is already performed. But you call it again, thus you get duplicated registration error.

To solve it, do the initialization in a static constructor, or store a flag (static bool) whether you initialized already, and skip initialization if it's true (and take care of thread safety).

Mikhail Shilkov
  • 34,128
  • 3
  • 68
  • 107
  • 1
    Better just do it at application start-up, a static constructor or somesuch. A mutating a static property like that will open you up to a world of potential thread safety issues. – Max Jul 28 '17 at 08:29
  • @Mikhail : Now I've put my Initialization in a separate class and build to generate a DLL.So that I can able to add the DLL in my Project Reference and call in my start-up. But unfortunately, it's not working. Is my way correct ? – Jayendran Dec 20 '17 at 10:35
  • @JayendranRosh Should work. If you are stuck, make a new question with steps to reproduce the issue. – Mikhail Shilkov Dec 20 '17 at 10:38
  • @Mikhail Never mind.I got the answer for my question https://stackoverflow.com/questions/44965314/can-you-create-an-initialize-method-for-dll/44965369 . Thanks for your quick response – Jayendran Dec 20 '17 at 11:16
  • @MikhailShilkov Can you provide some explanation on how to take care of thread safety issues? – cbrawl Apr 18 '19 at 16:56
0

I experienced the same issue with an Azure Function triggered from messages coming into an Event Hub.

My solution is probably not 100% correct, but I'm unsure if the registration to the provider would be impacted after the token (from GetToken) expires. So I don't register until the first time I call an SQL query that needs the Azure Key Vault / Always Encrypted.

try
{
   cmd.ExecuteNonQuery();
}
catch (System.ArgumentException ex)
{
   if (ex.TargetSite.Name == "DecryptSymmetricKey" && ex.InnerException == null)
   {
                    InitializeAzureKeyVaultProvider() //Register the Provider
                    cmd.ExecuteNonQuery(); // Try the query again.
   }
}
Rob Barat
  • 23
  • 1
  • 4