How to securely connect to Azure from C# and run REST APIs
Three things that got me here. First, I learned about creating a Service Principle in Azure and that the credentials for that those can be used to login to Azure, for a given Tenant/Directory ID. See here “Create an Azure service principal with Azure PowerShell”, also here if you want to see how to create a service principle via the portal. Second, I had already done some work with Managed Service Identity (MSI) and I knew that I can grant/allow access to Azure features using MSI, like Key Vault. (*NOTE: I did read somewhere, I think that you can use MSI identities for authentication, like I will show using a service principle, but MSI doesn’t yet do it on all Azure features, a work in progress I assume).
- How to connect to a database from an Azure Function using Azure Key Vault
- Using Managed Service Identity (MSI) with an Azure App Service or an Azure Function
And third, lastly, I was trying to get a periodicRestart IIS configuration to work on an Azure App Service, but even after I got the XDT to be added to the effective configuration, see here “Making changes to the applicationHost.config on Azure App Service”, it was not honored. I knew this, but there is no WAS in the Azure App Service product and WAS is what executes that IIS feature. I wanted to perform this operation “Web Apps - Restart” without storing any real or personally identifiable credentials. This is why I chose a service principle identity. And I did it!! Here’s how:
- Create the Service Principle account and password
- Store the account id and password in Azure Key Vault
- Enable MSI and grant the Azure App Service or Azure Function access to the Key Vault
- Code it
- Deploy / Run it
Create the Service Principle account and password
I pretty much did exactly what is described here “Use portal to create an Azure Active Directory application and service principal that can access resources”. So no need to redo that. What I would like to call out is that the Application ID is the USERID and the value of the Key you create is the password. You also need the Directory/Tenant ID for later, so note that down. Don’t confuse that with an Azure App Service Tenant, in this context the Tenant means your active directory id…it looks like a GUID.
Store the account id and password in Azure Key Vault
I wrote how to do this here “Create an Azure Key Vault and Secret”. So, that should work. I created 2, one for the ID and one for the PASSWORD, as seen in Figure 1. Figure 1, how to run any AZURE REST API securely
Enable MSI and grant the Azure App Service or Azure Function access to the Key Vault
Man, I have already done most of this, I though this was going to be a long one but it’s working out to be pretty quick. Isn’t is great how all this comes together? It is simply proof that the people creating all these features have a cross vertical collaborated plan! I wrote how to achieve this here “Using Managed Service Identity (MSI) with an Azure App Service or an Azure Function”. I did hit a small snag here, but is came up during my coding. I started getting a 403 (Forbidden) when I ran my code. I thought somthing had changed and spent some hours trouble shooting this. I decided to use Postman which I disucss here “Using Postman to call Azure REST APIs”, to see if I could get a better error, because I was simply getting a 403 and nothing more. I got a better answer…
{
"error": {
"Code": "Forbidden",
"message": "Operation \"get\" is not allowed by vault Policy",
"innererror": {
"Code": "ForbiddenByPolicy"
}
}
}
It is kind of clear, but I gave GET access to the Key for the application…and that was my problem. I gave GET access to the Key and not the Secret, bahhhhhh. OK, let’s move on…
Code it
Now the fun part. I coded this as an .NET Core console application initially because I wanted it to run as a scheduled WebJob. The code would work as a Timer Triggered Azure Function as well, but with some extra steps…maybe later I’ll share that in details. Getting the Secrets from Azure Key Vault is the same code as I wrote here “How to connect to a database from an Azure Function using Azure Key Vault”. So reuse that to get the application id and password key for the service principle which will run the Azure REST API. In order to run the REST API that will restart the Azure App Service, I needed to get an Authentication token, I achieved this with the following method.
private static async Task<string> GetAccessToken(string tenantId, string clientId, string clientKey)
{
WriteLine("Begin GetAccessToken");
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId, clientKey);
var result = await authenticationContext
.AcquireTokenAsync("https://management.azure.com/", credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
You need to include the “Microsoft.IdentityModel.Clients.ActiveDirectory” package into the project. It is installable via NuGet…love that NuGet stuff BTW. This method receives the tenant id which I mentioned earlier, I.e. the Azure Active Directory directory id, the application id and the password key of the service principle. The method returns the token. Then you can run the REST API, which I did like this.
private static async Task<string> RestartWebApp(string token)
{
WriteLine("Begin RestartWebApp");
string URL = "https://management.azure.com/subscriptions/{subscriptionId}" +
"/resourceGroups/{RGName}/providers/Microsoft.Web/sites/" +
"{siteName}/restart?softRestart=false&synchronous=false?" +
"api-version=2016-08-01";
httpClient.DefaultRequestHeaders.Remove("Authorization");
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
HttpResponseMessage response = await httpClient.PostAsync(URL, null);
return response.StatusCode.ToString();
}
And there you go, that’s it. I schedule the code to run every 24 hours and that’s it…a periodic restart. How to schedule a restart of your azure app service web app.
Deploy / Run it
I explain how to deploy and run a .NET Core as a WebJob here “How to deploy a .NET Core console application to Azure, WebJob”. Make sure to add/include a run.cmd or one of the other file types required to run a WebJob. Once run, I can look in he Activity log of the App Service and see that indeed, the Web App was restarted, Figure 2. Figure 2, how to run any AZURE REST API securely Notice that the WebJob was triggered by me, but the restart was run using the service principle identity. Very cool and you can call any REST API using this.