Retreive the host key from within my azure function - azure

To read an Application setting in Azure function I can do
Environment.GetEnvironmentVariable("MyVariable", EnvironmentVariableTarget.Process);
Is it possible to get a Host key in a similar way? I like to identify the caller of my azure function based on the key they are using but hate to have a copy of this key in Application settings

You could install Microsoft.Azure.Management.ResourceManager.Fluent and Microsoft.Azure.Management.Fluent to do that easily.
The following is the demo that how to get kudu credentials and run Key management API .I test it locally, it works correctly on my side.
For more detail, you could refer to this SO thread with C# code or use powershell to get it.
string clientId = "client id";
string secret = "secret key";
string tenant = "tenant id";
var functionName ="functionName";
var webFunctionAppName = "functionApp name";
string resourceGroup = "resource group name";
var credentials = new AzureCredentials(new ServicePrincipalLoginInformation { ClientId = clientId, ClientSecret = secret}, tenant, AzureEnvironment.AzureGlobalCloud);
var azure = Azure
.Configure()
.Authenticate(credentials)
.WithDefaultSubscription();
var webFunctionApp = azure.AppServices.FunctionApps.GetByResourceGroup(resourceGroup, webFunctionAppName);
var ftpUsername = webFunctionApp.GetPublishingProfile().FtpUsername;
var username = ftpUsername.Split('\\').ToList()[1];
var password = webFunctionApp.GetPublishingProfile().FtpPassword;
var base64Auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{username}:{password}"));
var apiUrl = new Uri($"https://{webFunctionAppName}.scm.azurewebsites.net/api");
var siteUrl = new Uri($"https://{webFunctionAppName}.azurewebsites.net");
string JWT;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", $"Basic {base64Auth}");
var result = client.GetAsync($"{apiUrl}/functions/admin/token").Result;
JWT = result.Content.ReadAsStringAsync().Result.Trim('"'); //get JWT for call funtion key
}
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + JWT);
var key = client.GetAsync($"{siteUrl}/admin/functions/{functionName}/keys").Result.Content.ReadAsStringAsync().Result;
}
The output:

Related

Programmatically authenticate AKS with Azure AD and Managed Identity

I'm new to AKS and the Azure Identity platform. I have an AKS cluster that is using the Azure AD integration. From an Azure VM that has a user assigned managed identity, I'm trying to run a C# console app to authenticate against Azure AD, get the kubeconfig contents and then work with the kubernetes client to perform some list operations. When the code below is run I get an Unauthorized error when attempting to perform the List operation. I've made sure that in the cluster access roles, the user assigned managed identity has the Owner role.
The code does the following:
Creates an instance of DefaultAzureCredential with the user managed identity ID
Converts the token from DefaultAzureCredential to an instance of Microsoft.Azure.Management.ResourceManager.Fluent.Authentication.AzureCredentials and authenticates
Gets the contents of the kubeconfig for the authenticated user
Gets the access token from http://169.254.169.254/metadata/identity/oauth2/token
Sets the access token on the kubeconfig and creates a new instance of the Kubernetes client
Attempt to list the namespaces in the cluster
I've pulled information from this POST as well from this POST.
I'm not sure if the scopes of TokenRequestContext is correct and if the resource parameter of the oauth token request is correct.
string userAssignedClientId = "0f2a4a25-e37f-4aba-942a-5c58f39eb136";
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId });
var defaultToken = credential.GetToken(new TokenRequestContext(new[] { "https://management.azure.com/.default" })).Token;
var defaultTokenCredentials = new Microsoft.Rest.TokenCredentials(defaultToken);
var azureCredentials = new Microsoft.Azure.Management.ResourceManager.Fluent.Authentication.AzureCredentials(defaultTokenCredentials, defaultTokenCredentials, null, AzureEnvironment.AzureGlobalCloud);
var azure = Microsoft.Azure.Management.Fluent.Azure.Authenticate(azureCredentials).WithSubscription("XXX");
var kubeConfigBytes = azure.KubernetesClusters.GetUserKubeConfigContents(
"XXX",
"XXX"
);
var kubeConfigRaw = KubernetesClientConfiguration.LoadKubeConfig(new MemoryStream(kubeConfigBytes));
var authProvider = kubeConfigRaw.Users.Single().UserCredentials.AuthProvider;
if (!authProvider.Name.Equals("azure", StringComparison.OrdinalIgnoreCase))
throw new Exception("Invalid k8s auth provider!");
var httpClient = new HttpClient();
var token = string.Empty;
using (var requestMessage =
new HttpRequestMessage(HttpMethod.Get, $"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={Uri.EscapeUriString("6dae42f8-4368-4678-94ff-3960e28e3630/.default")}&client_id={userAssignedClientId}"))
{
requestMessage.Headers.Add("Metadata", "true");
var response = await httpClient.SendAsync(requestMessage);
token = await response.Content.ReadAsStringAsync();
Console.WriteLine(token);
}
var tokenNode = JsonNode.Parse(token);
authProvider.Config["access-token"] = tokenNode["access_token"].GetValue<string>();
authProvider.Config["expires-on"] = DateTimeOffset.UtcNow.AddSeconds(double.Parse(tokenNode["expires_in"].GetValue<string>())).ToUnixTimeSeconds().ToString();
var kubeConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubeConfigRaw);
var kubernetes = new Kubernetes(kubeConfig);
var namespaces = kubernetes.CoreV1.ListNamespace();
foreach (var ns in namespaces.Items)
{
Console.WriteLine(ns.Metadata.Name);
var list = kubernetes.CoreV1.ListNamespacedPod(ns.Metadata.Name);
foreach (var item in list.Items)
{
Console.WriteLine(item.Metadata.Name);
}
}
Any help is appreciated!
Try using the resource in the token request without /.default.
So it should be:
resource=6dae42f8-4368-4678-94ff-3960e28e3630

Unable to Create Pool using Azure Batch Management Library with ServiceClientCrenetials generated via AzureCredentialsFactory

I am using this documentation to assign a managed identity to my Batch Pool. For simplicity I do not include that assignment in the examples below as the issue is not tied to that but rather to accessing the management library with my credentials and just being able to generate a new pool on my existing batch account.
I'm using a Service Principal to generate ServiceClientCredntials via AzureCredentialsFactory to use with the Microsoft.Azure.Management.Batch library.
The Service Principal has Azure Service Management permissions enabled with user_impersonation set as Delegated. It is also assigned as a Contributor role on the subscription.
I am able to create the credentials with the following code
using Microsoft.Azure.Management.Batch.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
string subscriptionId = "<subscriptionId>";
string tenantId = "<tenantId>";
string servicePrincipalId = "<servicePrincipalId>";
string servicePrincipalKey = "<servicePrincipalKey>";
var creds = new AzureCredentialsFactory().FromServicePrincipal(servicePrincipalId, servicePrincipalKey, tenantId, AzureEnvironment.AzureGlobalCloud).WithDefaultSubscription(subscriptionId);
var managementClient = new Microsoft.Azure.Management.Batch.BatchManagementClient(creds);
However when I attempt to use the credentials to create a pool in my Azure Batch account I get the following exception:
Microsoft.Rest.ValidationException: ''this.Client.SubscriptionId' cannot be null.'
I'm using .WithDefaultSubscription(subscriptionId) and have verified that the default subscription is set on the credentials prior to creating the BatchManagementClient.
Here is the code I am using to create the pool
var poolId = "test-pool";
var batchResourceGroupName = "<resourceGroupName>";
var batchAccountName = "<batchAccountName>";
var poolParameters = new Pool(name: poolId)
{
VmSize = "STANDARD_D8S_V3",
DeploymentConfiguration = new DeploymentConfiguration
{
VirtualMachineConfiguration = new VirtualMachineConfiguration(
new ImageReference(
"Canonical",
"UbuntuServer",
"18.04-LTS",
"latest"),
"batch.node.ubuntu 18.04")
}
};
var pool = await managementClient.Pool.CreateWithHttpMessagesAsync(
poolName: poolId,
resourceGroupName: batchResourceGroupName,
accountName: batchAccountName,
parameters: poolParameters,
cancellationToken: default(CancellationToken)).ConfigureAwait(false);
I am able to list my subscriptions with the credentials using
IAzure azure = Azure.Authenticate(creds).WithDefaultSubscription();
var subscriptions = azure.Subscriptions.List().ToList();
And I see the subscriptions that this service principal has access to in that list. So I know the credentials are good.
The exception occurs on this line
var pool = await managementClient.Pool.CreateWithHttpMessagesAsync(...
And here is the FULL CODE
using Microsoft.Azure.Management.Batch.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
string subscriptionId = "<subscriptionId>";
string tenantId = "<tenantId>";
string servicePrincipalId = "<servicePrincipalId>";
string servicePrincipalKey = "<servicePrincipalKey>";
var poolId = "test-pool";
var batchResourceGroupName = "<resourceGroupName>";
var batchAccountName = "<batchAccountName>";
var poolParameters = new Pool(name: poolId)
{
VmSize = "STANDARD_D8S_V3",
DeploymentConfiguration = new DeploymentConfiguration
{
VirtualMachineConfiguration = new VirtualMachineConfiguration(
new ImageReference(
"Canonical",
"UbuntuServer",
"18.04-LTS",
"latest"),
"batch.node.ubuntu 18.04")
}
};
var creds = new AzureCredentialsFactory().FromServicePrincipal(servicePrincipalId, servicePrincipalKey, tenantId, AzureEnvironment.AzureGlobalCloud).WithDefaultSubscription(subscriptionId);
var managementClient = new Microsoft.Azure.Management.Batch.BatchManagementClient(creds);
var pool = await managementClient.Pool.CreateWithHttpMessagesAsync(
poolName: poolId,
resourceGroupName: batchResourceGroupName,
accountName: batchAccountName,
parameters: poolParameters,
cancellationToken: default(CancellationToken)).ConfigureAwait(false);
---- UPDATE ----
I added the following line before calling Pool.CreateWithHttpMessageAsync()
managementClient.SubscriptionId = subscriptionId;
And am no longer getting an error regarding null subscriptionId and can now use the management client as expected.

Scope for Accessing Storage Account using Managed Identity

I'm using managed identity to access azure database in this manner.The Azure App Registration is used for getting the token and the token is passed to the connection.In the same manner,how do i connect to a storage account and write to a container? What will be the scope in this case?
AuthenticationResult authenticationResult = null;
var _app = ConfidentialClientApplicationBuilder.Create(Environment.GetEnvironmentVariable("ClientId"))
.WithAuthority(string.Format(Environment.GetEnvironmentVariable("AADInstance"), Environment.GetEnvironmentVariable("Tenant")))
.WithClientSecret(Environment.GetEnvironmentVariable("ClientSecret")).Build();
authenticationResult = _app.AcquireTokenForClient(new string[] { "https://database.windows.net/.default" }).ExecuteAsync().Result;
using (SqlConnection conn = new SqlConnection(Environment.GetEnvironmentVariable("DBConnection")))
{
conn.AccessToken = authenticationResult.AccessToken;
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT * FROM mytable", conn))
{
var result = cmd.ExecuteScalar();
Console.WriteLine(result);
}
}
Azure Storage uses this scope:
https://storage.azure.com/.default
That said, with the new Azure Storage SDK and Azure.Identity, you don't actually need to know this.
You can use them like this:
var credential = new ClientSecretCredential(tenantId: "", clientId: "", clientSecret: "");
var blobUrl = "https://accountname.blob.core.windows.net";
var service = new BlobServiceClient(new Uri(blobUrl), credential);
var container = service.GetBlobContainerClient("container");
var blob = container.GetBlobClient("file.txt");
// TODO: Write the file
For Azure Storage, the scope will be https://storage.azure.com/.default.
Please see this link for more details: https://learn.microsoft.com/en-us/azure/storage/common/storage-auth-aad-app?tabs=dotnet#azure-storage-resource-id.

Create Keys in Azure Key Vault by using API

I was created azure key vault through in the specified subscription. Followed this article,
https://learn.microsoft.com/en-us/rest/api/keyvault/keyvaultpreview/vaults/createorupdate#examples
And when the api called, azure vault created successfully. Now I also need to create a key for the created Key vault. Is it possible to create the key when the azure key vault creation?
Is it possible to create the key when the azure key vault creation?
As juunas said, you need to make a separate call to achieve what you want.
I test it with the following code, it works correctly on my side. The resourceUri is https://vault.azure.net. For more details, you could refer to this SO thread.
In Key vault channel, you need to Add policies to your registered application or user. And in Access Control you need to add permission to your registered application or user.
var appId = "0000000000000000000000000000000";
var secretKey = "******************************************";
var tenantId = "0000000000000000000000000000000";
var context = new AuthenticationContext("https://login.windows.net/" + tenantId);
ClientCredential clientCredential = new ClientCredential(appId, secretKey);
var tokenResponse = context.AcquireTokenAsync("https://vault.azure.net", clientCredential).Result;
var accessToken = tokenResponse.AccessToken;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
var requestURl = "https://xxxxxx.vault.azure.net/keys/xxxx/create?api-version=2016-10-01";
string body = "{\"kty\": \"RSA\"}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/json");
var response = client.PostAsync(requestURl, stringContent).Result;
}

How to enumerate Azure subscriptions and tenants programmatically?

How to enumerate Azure subscriptions and tenants programmatically? This is related to my previous question Login-AzureRmAccount (and related) equivalent(s) in .NET Azure SDK.
Basically I try to replicate the behavior of Login-AzureRmAccount and Get-AzureRmSubscription in desktop or a console application. Thus far I've figured out MSAL seems to always require client ID and tenant ID, so there needs to be some other library to acquire those from. After this I would like to go about creating a service principal programmatically using the most current library, but I suppose that is a subject for further investigation (and questions if needed).
Actually, the Login-AzureRmAccount and Get-AzureRmSubscription use the Microsoft Azure PowerShell app to operate the Azure resource through Resource Manager REST APIs.
To simulate the same operations using REST as PowersShell commands, we can also use this app. However since this app is register on Azure portal(not the v2.0 app) so we are not able to acquire the token using this app via MSAL. We need to use Adal instead of MSAL.
Here is a code sample to list the subscriptions using admin account via Microsoft.WindowsAzure.Management using this app for your reference:
public static void ListSubscriptions()
{
string authority = "https://login.microsoftonline.com/common";
string resource = "https://management.core.windows.net/";
string clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
Uri redirectUri = new Uri("urn:ietf:wg:oauth:2.0:oob");
AuthenticationContext authContext = new AuthenticationContext(authority);
var access_token = authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters (PromptBehavior.Auto)).Result.AccessToken;
var tokenCred = new Microsoft.Azure.TokenCloudCredentials(access_token);
var subscriptionClient = new SubscriptionClient(tokenCred);
foreach (var subscription in subscriptionClient.Subscriptions.List())
{
Console.WriteLine(subscription.SubscriptionName);
}
}
Update:
string resource = "https://management.core.windows.net/";
string clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
string userName = "";
string password = "";
HttpClient client = new HttpClient();
string tokenEndpoint = "https://login.microsoftonline.com/common/oauth2/token";
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={userName}&password={password}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");
var result = client.PostAsync(tokenEndpoint, stringContent).ContinueWith<string>((response) =>
{
return response.Result.Content.ReadAsStringAsync().Result;
}).Result;
JObject jobject = JObject.Parse(result);
var token = jobject["access_token"].Value<string>();
client.DefaultRequestHeaders.Add("Authorization", $"bearer {token}");
var subcriptions = client.GetStringAsync("https://management.azure.com/subscriptions?api-version=2014-04-01-preview").Result;
Console.WriteLine(subcriptions);

Resources