Exercise - Configure a custom application in .NET by using managed identity

Completed

You can store application connection strings, credentials, passwords, and other sensitive information in Azure Key Vault. To access Key Vault, you need to provide credentials. Now as part of the application move to an Azure VM, you can update it to use managed identities to get an access token to authenticate to Key Vault.

Your stock-tracking application connects to Azure SQL Database. Previously, your on-premises app stored the connection string in configuration files. As part of the migration to Azure, you now securely store all credentials and connection strings in an Azure Key Vault.

Your stock-tracking application runs on a virtual machine (VM), so you can use the system-assigned managed identity that you created. To your key vault, you add a policy that grants the appropriate permissions to the VM.

In this exercise, you'll edit your app to use the new key vault. Then, you'll grant the managed identity for the VM access so that the app can retrieve the database connection. Finally, you'll build and run an app on the VM to access Key Vault, and retrieve the information.

Create an app to fetch secret information from Key Vault

  1. Sign in to the VM that you created earlier.

    ssh $publicIP
    

    Note

    If the environment variable publicIP isn't set, reset it by running the following command.

    export publicIP=$(az vm show \
        --name prodserver \
        --resource-group <rgn>[Sandbox resource group]</rgn> \
        --show-details \
        --query [publicIps] \
        --output tsv)
    

    Then, rerun the ssh $publicIP command.

  2. Move to the identity/secretapp folder.

    cd ~/identity/secretapp
    
  3. Open the Program.cs file by using the nano editor.

    nano Program.cs
    

    This file contains the C# source code for the sample app.

  4. Examine the Main method.

    static async Task Main(string[] args)
    {
        await GetSecretFromKeyVault().ConfigureAwait(false);
    }
    

    This method is the entry point of the application which just calls the GetSecretFromKeyVault method.

  5. Go down to the GetSecretFromKeyVault method. Examine the method's first block of code.

    private static async Task GetSecretFromKeyVault()
    {
        var keyVaultName = "<key vault name>";
        Uri keyVaultUri = new Uri($"https://{keyVaultName}.vault.azure.net");
    
        SecretClient secretClient = new SecretClient(keyVaultUri, new DefaultAzureCredential());
        ...
    }
    

    Replace "<key vault name>" with your key vault name. This code uses the DefaultAzureCredential to authenticate the client that will make the request. Behind the scenes, this code obtains the system-managed identity for the VM that runs the code. It then generates an instance of SecretClient that will utilize this authentication scheme. You can use this SecretClient instance to access secrets in the key vault.

  6. Look at the next part of the code.

    var keyVaultSecretName = "<secret name>";
    
    try
    {
        var secret = await secretClient.GetSecretAsync(keyVaultSecretName).ConfigureAwait(false);
    
        Console.WriteLine($"Secret: {secret.Value}");
    }
    catch (Exception exp)
    {
        Console.WriteLine($"Something went wrong: {exp.Message}");
    }
    

    Replace <secret name> with "DBCredentials", which is the name of the secret that you created in the key vault.

    This block of code calls the GetSecretAsync method of the SecretClient to retrieve a specific secret and display its value. If the client doesn't have permission to access the key, then this code throws an exception, and displays an error message.

    Note

    No password, certificate, or client secret is stored in the code.

  7. To save your changes, press Ctrl+O, and then press Enter.

  8. To close the nano editor, press Ctrl+X.

Test the application

  1. Run the following command to build the application.

    dotnet restore
    dotnet build
    

    If you correctly edited the program, the app should build without errors.

  2. Run the application.

    dotnet run
    

    You haven't yet authorized the VM's service principal to access your key vault. Therefore, the application should respond with an error message:

    Something went wrong: Operation returned an invalid status code 'Forbidden'

Grant permissions to enable the service principal to retrieve secrets from Key Vault

  1. Close your connection to the VM.

    exit
    
  2. In the previous exercise, you noted the principal ID for your VM. If you don't remember the principal ID, then run the following command to find the system-assigned ID for your VM. (The $VMNAME variable was set in the prior exercise to a value of prodserver.)

    az vm identity show \
      --name $VMNAME \
      --resource-group <rgn>[Sandbox resource group]</rgn>
    

    The following code is an example of the returned value. Your IDs will differ.

    {
        "principalId": "aba6da53-9180-47fc-8fc4-4b35f154e845",
        "tenantId": "a95baa51-dcb1-4b9a-8312-8774a8afddbe",
        "type": "SystemAssigned",
        "userAssignedIdentities": null
    }
    

    Use this value to return only the principal ID in the next command.

  3. Use the principal ID to authorize the VM to retrieve and list secrets in your key vault.

    az keyvault set-policy \
        --name $KVNAME \
        --secret-permissions get list \
        --object-id $(az vm identity show \
                        --name $VMNAME \
                        --resource-group <rgn>[Sandbox resource group]</rgn> \
                        --output tsv \
                        --query principalId)
    

    The returned value is in JSON format. It contains the ID, location, name, and all of the associated properties.

Test the application again

  1. Sign in to your VM.

    ssh $publicIP
    
  2. Change to the identity/secretapp folder.

    cd ~/identity/secretapp
    
  3. Run the application.

    dotnet run
    

    This time, the application should retrieve the secret from Key Vault and display its value.

    Database connection string:: Server=tcp:prodserverSQL.database.windows.net,1433;Database=myDataBase;User ID=mylogin@myserver;Password=examplePassword;Trusted_Connection=False;Encrypt=True;