0

I'm new to using C# in Azure and I'm running an inline C# script in an Azure Logic Apps workflows to read a Key Vault secret into a variable from within a "CSharp Script Code" object in an Azure Workflow. But nothing I’ve read on the internet appears to work. Should this be a simple one-line command as I’ve been told?

I have a secret named HMAC connected to the HMACKey name in the workflow and was told to use the code

string secretKey = await {Azure Key Vault Client}.GetSecretAsync({Azure Key Vault Url}, {Azure Secret})

to retrieve this.

I'm assuming {Key Vault Url} means the first part of the URI for the Secret Identifier, which in my test case is https://hmactesting.vault.azure.net, and the secret is the name of the secret HMAC in this case.

But what is meant by {Azure Key Vault Client} in this instance? Is this a new KeyVaultClient() type that I have to declare and authorise in the inline code, or does this already exist as an environment variable within the workflow that I can attach to and use?

I’ve searched the net but can’t find anything specifically relating to how to do this from within an inline "CSharp Script Code" object in an Azure Workflow?

I believe the strings for KeyVaultUrl and secret are correct in the command, which leaves the {Azure Key Vault Client} object. Is this something still I have to create in the code? I’ve seen examples of C# code where this is created, but those were not using the Azure workflow and C# scripting object.

I can already see the secret I'm trying to read when I set it up as an object in the workflow, so can I connect to this from within the C# script?

My full test script is as follows:

// Add the required libraries
#r "Newtonsoft.Json"
#r "Microsoft.Azure.Workflows.Scripting"
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Workflows.Scripting;
using Newtonsoft.Json.Linq;
using System;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

/// <summary>
/// Executes the inline csharp code.
/// </summary>
/// <param name="context">The workflow context.</param>
/// <remarks> This is the entry-point to your code. The function signature should remain unchanged.</remarks>
public static async Task<Results> Run(WorkflowContext context, ILogger log)
{
  //String Variables (for clarity)
  string KeyVaultUrl = "https://hmactesting.vault.azure.net";
  string secret = "HMAC";
  string compare = string.Empty;
  string _origonalMessage = string.Empty;
  string _origonalHmac = string.Empty;
  string _calculatedHmac = string.Empty;

  ////Define the trigger output.
  var triggerOutputs = (await context.GetTriggerResults().ConfigureAwait(false)).Outputs;
  //string kvSecret = await kVClient.GetSecretAsync(azureKeyVaultUrl, secret);

  ////Set the local HMAC key from the Azure Key Vault.
  string secretKey = "HMAC-TEST"; // <--Temporary for testing. 
  //I need to get the above value from the 'HMAC' secret in the 'HMACTesting' Key Vault
  //The example I was given was to use: string secretKey = await {Azure Key Vault Client}.GetSecretAsync({Azure Key Vault Url}, {Azure secret});
  //Assuming I have the correct {Key Vault Url} I need to know what is meant by {Key Vault Client} do I need to build this?
  //var key = await ((WHAT?)).GetSecretAsync(KeyVaultUrl, secret) // <-- Should this work? If yes, could it be a permissions thing? Although I can see the secret contents when using an object on the workflow.


  ////Set the whole JSON body text  from the trigger.
  var origonalMessage = triggerOutputs["body"].ToString();
  _origonalMessage = origonalMessage;

  ////Set the remote HMAC encrypted text from the Azure header. (needs to be the input header)
  var receivedHmac = triggerOutputs?["headers"]?["HMACEncrypted"]?.ToString();
  _origonalHmac = receivedHmac;

  ////Encrypt the body text with the local HMAC key and compaire with the remote encrypted text.
  //convert key and message to byte arrays
    byte[] keyBytes = Encoding.UTF8.GetBytes(secretKey);
    byte[] messageBytes = Encoding.UTF8.GetBytes(origonalMessage);

  //Create an HMAC SHA256 instance with the local key
    using (HMACSHA256 hmac = new HMACSHA256(keyBytes))
    {
        //Compute the HMAC and convert it to a string
        byte[] hashBytes = hmac.ComputeHash(messageBytes);
        var localHmac = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
    _calculatedHmac = localHmac;
  }

  //Compare the computed HMAC with the received HMAC
  string testString = "GAURAV";
  if (_calculatedHmac.Equals(receivedHmac, StringComparison.OrdinalIgnoreCase))
  {
    compare = "Match";
  }
  else
  {
    compare = "noMatch";
;
  }


  return new Results
  {
    Message = compare 
  };
}

public class Results
{
  public string Message {get; set;}
}

The rest of this works as expected apart from getting the key vault value. Is there a straightforward way to read the secret from the key vault?

I realise this is probably a very simple question but just can’t find anything relatable to the inline C# code when searching. Any help would be appreciated.

6
  • Are you using consumption logic app or standard logic app? Commented Dec 17, 2024 at 11:08
  • "But this just errors." - Please edit the question and provide the full error(s) as text Commented Dec 17, 2024 at 11:24
  • The workflow is in a standard logic app. Commented Dec 17, 2024 at 11:36
  • You can fetch the secret value using Environment variables. Are you fine with this approach? @Garry_G Commented Dec 17, 2024 at 15:16
  • 1
    Sorry but why would you want to take that approach? There are at least three other ways I would get the value before using the approach you’re talking about here. Commented Dec 17, 2024 at 19:39

3 Answers 3

2

Your approach is a very long winded way to get the secret value. LogicApps is a low code environment for a reason so why write code to do this?

Firstly, use the first answer as a reference for how to setup the managed identity to have access to the keyvault and the secret, that’s number one.

Now, rather than using the inline C# operation, you should just get the value directly via the appsetting function.

https://techcommunity.microsoft.com/blog/integrationsonazureblog/programmatically-accessing-app-settings-from-logic-apps-expressions/3680407

Literally just create the expression in a Compose or Initialize Variable operation.

So for completion, create the environment variable using the reference notation as specified in the fourth point in the first answer and then you can use the appsetting function in an expression by simply passing in the name of that environment variable and it will give you the value.

e.g. ... appsetting('HMACKey')

Additional reference ... https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references?tabs=azure-cli

Sign up to request clarification or add additional context in comments.

2 Comments

I haven't added the key vault read part to this yet, just using a temporary (string secretKey = "HMAC-TEST";) command for testing. I need to use the C# approach because the secret value is needed for the rest of the code. The rest of the script is concerned with HMAC encryption checking between local and remote clients and works. I can already see the secret value in the workflow using a 'get secret' object. Are you saying I can use this in the script by calling appsetting('HMACKey'). Sorry, I'm from a C# background and am new to Azure Workflow.
Don’t be sorry. No, I’m not saying that but you should be able to pass the value of the appsetting result to the C# script. I’d advise you to write as little code as possible but that’s up to you. If you use the get secret action from the KeyVault connector, each call will cost you a retrieve, using the appsettings doesn’t.
0
  • Execute CSharp Script Code action in standard logic app is currently in preview and reference of some assemblies might not be supported at this moment.

  • I have used the Environment Variable approach to fetch the secret value from key vault.

  • In order to do so, first you need to grant the secret permissions to the logic app's managed identity in key vault's Access policies or you can grant RBAC role too.

  • Then, add the key vault reference in @Microsoft.KeyVault(SecretUri=https://{keyvaultName}.vault.azure.net/secrets/{secretName}) format as shown below.

enter image description here

enter image description here

  • Use the below given code in Execute CSharp Script Code action.
#r "Newtonsoft.Json"
#r "Microsoft.Azure.Workflows.Scripting"
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Workflows.Scripting;
using System;

public static Results Run(WorkflowContext context, ILogger log)
{
    string secretValue = GetEnvironmentVariable("HMACSecretKey");

    log.LogInformation($"Fetched Environment Variable: {secretValue}");

    return new Results
    {
        Message = $"The secret value is: {secretValue}"
    };
}

public static string GetEnvironmentVariable(string name)
{
    return System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
}

public class Results
{
    public string Message { get; set; }
}
  • You will be able to get the secret value successfully.

enter image description here

7 Comments

So when you say "HMACSecretKey" do you mean the string part in the Secret identifier?
This is the field name of environment variable which holds the key vault secret reference, it can be anything
Have you completed this step? You need to provide it in Environment variable in @Microsoft.KeyVault(SecretUri=https://HMACTesting.vault.azure.net/secrets/HMAC) format.
I thought I did but I obviously didn't do the right thing. I went into the 'Environment variables' under Settings in the Logic App and made a new variable then pasted in the string, but it's just showing the string. So I obviously don't know what you mean here! Sorry
Can you share the screenshot of what you are doing?
|
0

Directly answering your question as posed in the code comments:

Documentation describes SecretClient which would be what you would need in a general C# context. Usage would be something like the following:

string keyVaultName = Environment.GetEnvironmentVariable("KEY_VAULT_NAME");
var kvUri = "https://" + keyVaultName + ".vault.azure.net";

var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());

This is provided by the NuGet package Azure.Security.KeyVault.Secrets

However...

As mentioned in other comments and answers this is an approach that is more difficult than several other approaches available to you in Logic Apps, where you can either:

  • Read the secret using a Key Vault Reference in the App Service's Environment Variables and access this as an appsetting with @appsetting('name_of_variable')
  • Use the Key Vault Action to get the secret immediately per-run of the Logic App, in cases where the app setting doesn't refresh quickly enough for your needs: The delay is because App Service caches the values of the key vault references and refetches it every 24 hours.

3 Comments

Is this creating a new Secret in the vault? I don't want to create a new secret client I just need to read the contents of the 'HMAC' secret in the 'HMACTesting' vault. I can already see this in a workflow object. I just need to ether read this in the C# script or somehow pas this into the C# script. The script itself is concerned with HMAC testing between local and remote sites and works, except for getting the secret contents. Maybe I'm just explaining it wrong. Surely this should be a very simple thing to do!
@Garry_G No? It's creating a client. Nothing about creating a new secret. Question still stands as maybe I haven't got the point across, why do you want to use the Inline C# action to do this, when Logic Apps have a couple of different built-in methods that are easier?
The section after this in the documentation is talking about saving a secret with SetSecretAsync - but you don't have to do that if that's not what you want - the code sample is just creating a client.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.