Share the love

While modernizing an application or creating a cloud-native application, managing secrets becomes a very important task. This article will cover two different ways to fetch secrets from Azure Key Vault. PHP based App service is used for demonstrate the required steps. First, we will cover Windows App Service and then Linux App Service. Under this, the first method is using manual steps in portal and then using the PHP code to do the same. We will cover this later as to why PHP code might be required.

This is a tutorial on fetching secrets from key vaults using App Service MSI. As part of this testing, both windows based and Linux based applications would be covered.

Prerequisite

  • Azure App Service with VNet integration – Standard or above App Service Plan is required to enable VNet Integration.
  • Azure Keyvault with private endpoint in the same VNet

Steps For Windows App Service

  • Under Identity tab of App Service, enable system assigned identity.
  • Add Azure role assignments to provide access to key vault. We will add the role of Key Vault Secrets Officer for key vault “cietest”.
  • Add Access Policy in key vault. Provide access to Azure App Service.
  • Click on Configuration in App Service and add Application settings.
  • Add a Name and provide the Value. Name can be any user friendly name. For Value, use following syntax:
@Microsoft.KeyVault(SecretUri=https://<KV_Name>.vault.azure.net/secrets/<Secret_Name>/<Secret_Version>)

As seen from above screenshot, Key Vault Reference is added successfully. To sum it all up, we have an App Service with VNet Integration and MSI which has role of Key Vault Secrets Officer. We also have a Key Vault with private endpoint (deployed in same VNet). Access policy is added for app service to fetch the secret.

Alternatively, following syntax can also be used:

@Microsoft.KeyVault(VaultName=<KV_Name>;SecretName=<Secret_Name>)

Steps for Linux App Service

All the above steps remain valid. Then what’s the catch?

As of today, Linux based App Service have a known issue that they can’t use application settings to fetch secrets from key vaults over private endpoints. To get around this issue, all the above steps should be performed, along with following step:

  • Take Virtual IP Address of the App Service Properties and add it the Key Vault’s Firewall (under Networking).

If the environment variable in the application are still not loading up, then don’t forget to restart the App Service.

Steps to Fetch Key Vault Secret Using PHP code

Heading says it all! Just go ahead and use the following code to achieve the same. This can be useful if your application requirements state that you can’t use App Service virtual IP address.

<?php
namespace msft\keyvault;

class AzureKeyVault {

    private $kvUrl;

    function __construct($kvName) {
        if ($this->IsNullOrEmptyString($kvName)) {
            return false;
        }

        $this->kvUrl = "https://".$kvName.".vault.azure.net";
        return true;
    }

    function getSecret($secretName) {        
        $IdentityEndPoint = getenv('IDENTITY_ENDPOINT');
        $IdentitySecret = getenv('IDENTITY_HEADER');
        $url = $IdentityEndPoint."?resource=https%3A%2F%2Fvault.azure.net&api-version=2017-09-01";

        //open connection
        $ch = curl_init();
        // Set Options
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Secret: $IdentitySecret"));
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_NOPROXY, "*");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
        $bearer = curl_exec($ch);
       
        $token_pattern="/\"access_token\"\:\"(.*?)\"/";
        preg_match($token_pattern, $bearer, $matres);
        $token = $matres[1];        
        curl_close($ch);
        
        $ch1 = curl_init();
        $vaultUrl = $this->kvUrl."/secrets/".$secretName."?api-version=2016-10-01";
       
        // Set Options
        curl_setopt($ch1, CURLOPT_HTTPHEADER, array("Authorization: Bearer $token"));
        curl_setopt($ch1, CURLOPT_URL, $vaultUrl);
        curl_setopt($ch1, CURLOPT_NOPROXY, "*");
        curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); 

        
        $result = curl_exec($ch1);         
        $mat_pattern = "/\"value\"\:\"(.*?)\"/";
        preg_match($mat_pattern, $result, $match);
        curl_close($ch1);       
        return $match[1];
    }

    function IsNullOrEmptyString($str){
        return (!isset($str) || trim($str) === '');
    }
}

If you’ve any queries or questions, feel free to drop it in comments.