Azure Key Vault Add Access Policy with C# - c#

I am trying to retrieve all the Certificates, Keys and Secrets from a Key Vault in order to perform a compliance test of it´s settings. I was able to create a Key Vault Client using Azure Management SDK,
KeyVault Client objKeyVaultClient = new KeyVaultClient(
async (string authority, string resource, string scope) =>
{
...
}
);
and trying to retrieve the certificates / keys / secrets with:
Task<IPage<CertificateItem>> test = objKeyVaultClient.GetCertificatesAsync(<vaultUri>);
However, first I need to set the access policies with List and Get permissions. In PowerShell I achieve this with:
Set-AzKeyVaultAccessPolicy -VaultName <VaultName> -UserPrincipalName <upn> -PermissionsToKeys List,Get
Do you know a way that I can do the same in C#?

If you want to manage Azure key vault access policy with Net, please refer to the following steps
create a service principal (I use Azure CLI to do that)
az login
az account set --subscription "<your subscription id>"
# the sp will have Azure Contributor role
az ad sp create-for-rbac -n "readMetric"
Code
// please install sdk Microsoft.Azure.Management.Fluent
private static String tenantId=""; // sp tenant
private static String clientId = ""; // sp appid
private static String clientKey = "";// sp password
private static String subscriptionId=""; //sp subscription id
var creds= SdkContext.AzureCredentialsFactory.FromServicePrincipal(clientId,clientKey,tenantId,AzureEnvironment.AzureGlobalCloud);
var azure = Microsoft.Azure.Management.Fluent.Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(creds)
.WithSubscription(subscriptionId);
var vault = await azure.Vaults.GetByResourceGroupAsync("group name", "vault name");
await vault.Update().DefineAccessPolicy()
.ForUser("userPrincipalName")
.AllowKeyPermissions(KeyPermissions.Get)
.AllowKeyPermissions(KeyPermissions.List)
.Attach()
.ApplyAsync();

Related

Azure Key Vault and Certificate - .NET Framework ClientCertificateCredential access to Secrets

I have generated .pfx, .pvk and .cer certification files.
In Azure:
I created a new Vault, let's call it MyVault
In MyVault, I created a Secret called SubscriptionKey
MyVault has a Certificates section to which I've uploaded MyCertificate.cer file.
Confusingly enough, Azure also has a "Azure Active Directory" section where I can also upload Certificates. This is what I understood from researching, to be the place where to upload the certificate, and get the associated clientId and tenantId needed for the ClientCertificateCredential constructor.
Goal: Retrieve the secret value from MyVault using a Certificate and the code:
public static string GetSecretFromAzureKeyVault(string secretName)
{
string vaultUrl = "https://MyVault.vault.azure.net/";
string cerPath = "C:\\Personal\\MyCertificate.cer";
ClientCertificateCredential credential = new(
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
cerPath
);
SecretClient client = new(new Uri(vaultUrl), credential);
KeyVaultSecret secret = client.GetSecret(secretName);
return secret.Value;
}
When running the code I'm still getting null for the line:
KeyVaultSecret secret = client.GetSecret(secretName);
Any suggestions on what I've done wrong in this flow or regarding the resources?
EDIT:
Error screenshot:
I have followed the below steps and got the secret value
Create an app from AAD and register the app using APP registrations.
Create a keyVault and secret. And use the secret name in the code.
Use the ClientId and TenantId from the App registrations and use it in the code.
Download the .pfx format file and use the certificate in the code.
Use .pfx downloaded path in code
public static string GetSecretFromAzureKeyVault(string secretName)
{
string vaultUrl = "https://keyvault.vault.azure.net/";
string cerPath = "C:\\Tools\\keyvault-keycertificate-20230109.pfx";
ClientCertificateCredential credential =
new ClientCertificateCredential("TenantId", "ClientId", cerPath);
SecretClient client = new SecretClient(new Uri(vaultUrl), credential);
KeyVaultSecret secret = client.GetSecret(secretName);
return secret.Value;
}
You can find the secret value in the below highlighted screen.

Unable to fetch secret value from Key Vault using system managed identity in C# Azure Function

I had enabled system assigned managed identity in azure function(Service bus topic trigger) and added the identity(Object (principal) ID
) in key vault access policy with "Get,List" permissions of secrets, keys. I added the reference of the Key Vault into Azure function Application settings and able to receive at runtime after azure function deployment.
#Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931)
Code:
private async Task<string> FetchSecretValueFromKeyvault(string secretName)
{
_logger.LogInformation($"FetchSecretValueFromKeyvault: SecretName {secretName}");
string actualSecret = string.Empty;
try
{
string systemAssignedClientId = GetEnvironmentVariable("AzureADManagedIdentityClientId");
string azureKeyVaultUrl = GetEnvironmentVariable("AzureKeyVaultUrl");
var defaultAzureCredentialOptions = new DefaultAzureCredentialOptions();
defaultAzureCredentialOptions.ExcludeAzureCliCredential = true;
defaultAzureCredentialOptions.ExcludeEnvironmentCredential = true;
defaultAzureCredentialOptions.ExcludeAzurePowerShellCredential = true;
defaultAzureCredentialOptions.ExcludeInteractiveBrowserCredential = true;
defaultAzureCredentialOptions.ExcludeManagedIdentityCredential = false;
defaultAzureCredentialOptions.ExcludeSharedTokenCacheCredential = true;
defaultAzureCredentialOptions.ExcludeVisualStudioCodeCredential = true;
defaultAzureCredentialOptions.ExcludeVisualStudioCredential = true;
defaultAzureCredentialOptions.ManagedIdentityClientId = systemAssignedClientId;
var credential = new DefaultAzureCredential(defaultAzureCredentialOptions);
var client = new SecretClient(new Uri(AzureKeyVaultUrl)), credential);
var secret = await client.GetSecretAsync(secretName).ConfigureAwait(false);
actualSecret = secret.Value.Value;
_logger.LogInformation($"FetchSecretValueFromKeyvault: Received secretValue for {secretName}");
}
catch (RequestFailedException ex)
{
actualSecret = string.Empty;
_logger.LogError($"Message: {ex.Message}. \nInnerException:{ex.InnerException}. \nStackTrace: {ex.StackTrace}. \nInnerExceptionMessage:{ex.InnerException?.Message}.");
}
catch (Exception ex)
{
actualSecret = string.Empty;
_logger.LogError($"Message: {ex.Message}. \nInnerException:{ex.InnerException}. \nStackTrace: {ex.StackTrace}. \nInnerExceptionMessage:{ex.InnerException?.Message}.");
}
return actualSecret;
}
local settings & Azure Function App Settings:
"AzureADManagedIdentityClientId": "xxx-123-abc-xyz-567890"
"AzureKeyVaultUrl": "https://keyvaulturl.azurewebsites.net",
Nuget package and its versions:
Azure.Security.KeyVault.Secrets -- 4.3.0
Azure.Extensions.AspNetCore.Configuration.Secrets -- 1.2.2
Azure.Identity -- 1.6.1
Function Runtime Version: .NET Core V3.1
I am trying to read same secret value through code with help of same managed identity, I am getting error ManagedIdentityCredential authentication unavailable. Multiple attempts failed to obtain a token from the managed identity endpoint.' while debugging in local machine. I deployed azure function and in application insights, I am getting No Managed Identity found for specified ClientId/ResourceId/PrincipalId. Status: 400 (Bad Request)
I double cross checked PrincipalId, Its existed in both local seetings , azure function app settings and value is correct.
what am i doing wrong?
Please check :
When deployed to Azure resource that actually supports managed identity, the library automatically uses managed identities for Azure resources
Local machines does not support managed identities for Azure resources.
So in local environment Microsoft.Azure.Services.AppAuthentication library uses the developer credentials.
For local development, AzureServiceTokenProvider tries to fetch tokens first using Visual Studio, and then using Azure command-line interface (CLI), or Azure AD Integrated Authentication.
You can try to use connection string specified in the AzureServicesAuthConnectionString environment variable that can be passed to the AzureServiceTokenProvider .
You can use visual studio or azure cli method to pass to azure service authentication.
Using azure cli , you can create service principal rbac for local testing something like
az ad sp create-for-rbac --name local-sp --skip-assignment.
To use a connection string passed to the AzureServiceTokenProvider in the AzureServicesAuthConnectionString environment variable for Local development which uses AzureCli to get token.
`RunAs=Developer; DeveloperTool=AzureCli`
You may then have to add the service principal localtest-sp in the access control (IAM ) for the required Azure services.
Using the DefaultAzureCredential in Azure.Identity will provide some sort of similar type functionality to AzureServiceTokenProvider in AppAuthentication, where current environment can be changed.
Please check this App Authentication client library for .NET | Microsoft Docs to try for other options using secret or certificate.
Snippets from AppAuthentication
Using AppAuthentication library :
AzureServiceTokenProvider tokenProvider = new AzureServiceTokenProvider();
var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(tokenProvider.KeyVaultTokenCallback));
...
Using Azure.Identity library :
var client = new SecretClient(new Uri("https://keyvaultname.vault.azure.net"), new
DefaultAzureCredential());
var secret = client.GetSecret("secretName").Value;
For Access token retrival
Using AppAuthentication library:
var tokenProvider = new AzureServiceTokenProvider();
var accessToken = await tokenProvider.GetAccessTokenAsync(ResourceId);
Using Azure.Identity library:
var tokenCredential = new DefaultAzureCredential();
var accessToken = await tokenCredential.GetTokenAsync(
new TokenRequestContext(scopes: new string[] { ResourceId + "/.default" }) { }
);
Reference :
python - ManagedIdentityCredential authentication unavailable, no managed identity endpoint found - Stack Overflow

Authenticate at subscription level using Azure SDK

I'm trying to get a list of App Services from a subscription so that I can filter staging sites and delete them using the Azure SDK. I'm running into the issue of not authenticating properly or not being able to see any resources on the subscription.
I have the "Owner" role on the subscription so I should have total access to the subscription and its resources however when trying to follow Microsoft's docs on authenticating (https://learn.microsoft.com/en-us/dotnet/azure/sdk/authentication) I can't seem to find a way to authenticate at the subscription level.
Here's the code I have:
var azurecreds = SdkContext.AzureCredentialsFactory
.FromServicePrincipal(
"???", //client ID
"???", //client secret
"have this", //tenant ID
AzureEnvironment.AzureGlobalCloud);
var azure = Azure
.Configure()
.Authenticate(azurecreds)
.WithSubscription("have this"); //subscription ID
//attempts with hard-coded values but not working
var appServicePlans = azure.AppServices.AppServicePlans.List();
var appServicePlans2 = azure.WebApps.List();
var appServicePlans2 = azure.AppServices.AppServicePlans.ListByResourceGroup("Staging");
As you are following this document : Authenticate with token credentials
So , as per the above document , you must have created a service principal using this command :
az ad sp create-for-rbac --sdk-auth
After you have create this service principal , you will get the below details :
From the above picture you have to copy the ClientID, Client Secret, TenantID and SubscriptionId. After you have taken a note of these mentioned details , you can put the same in the code .
var azurecreds = SdkContext.AzureCredentialsFactory
.FromServicePrincipal(
"ClientID copied from the above step", //client ID
"Client Secret Copied from the above step", //client secret
"have this", //tenant ID
AzureEnvironment.AzureGlobalCloud);
var azure = Azure
.Configure()
.Authenticate(azurecreds)
.WithSubscription("have this"); //subscription ID
//attempts with hard-coded values but not working
var appServicePlans = azure.AppServices.AppServicePlans.List();
var appServicePlans2 = azure.WebApps.List();
var appServicePlans2 = azure.AppServices.AppServicePlans.ListByResourceGroup("Staging");

How to connect to azure cosmos db account in a c# code without using primary key

I have been trying to connect to Azure Cosmos DB account. The actual aim is to get the keys for testing purposes. So I cannot use keys to login into the cosmos DB account.
I found approaches online which are using the primary key to login but that is not my aim. Further, I found this approach on stack overflow using fluent SDK but it is not working for me.
Getting azure cosmos DB key programmatically
I found another way of certificate-based authentication here-Certificate Based authentication for cosmos db
I came across this command to fetch the primary key but the issue is that I am unable to connect to azure cosmos DB account through c# code which is not allowing me to fetch keys.
var cosmosPrimaryKey = _accountCosmosDBProvider.GetPrimaryKey(rgName, accountName, CancellationToken.None);
Does anyone have any idea on how to proceed for the same?
According to the information, I do a test on my side. We can use the following steps to get the private key.
Register an Azure AD application
Create the certificate-based credential
$cert = New-SelfSignedCertificate -CertStoreLocation "Cert:\CurrentUser\My" -Subject "CN=sampleAppCert" -KeySpec KeyExchange -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(10) -NotBefore (Get-Date).AddYears(-1)
$bin = $cert.RawData
$base64Value = [System.Convert]::ToBase64String($bin)
Connect-AzureAD -TenantId "<your tenant id>"
$app=Get-AzureADApplication -ObjectId < the object id of the app you create>
New-AzureADApplicationKeyCredential -ObjectId 77bfe399-38db-4ce5-85b1-c79ef0ed5e5b -CustomKeyIdentifier "key12" -Value $base64Value -Type AsymmetricX509Cert -Usage Verify -EndDate $cert.NotAfter
Configure your Azure Cosmos account to use the new identity
Code
# get the certificate
X509Certificate2 cert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates;
X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectName, "sampleAppCert", false);
cert = signingCert.OfType<X509Certificate2>().OrderByDescending(c => c.NotBefore).FirstOrDefault();
store.Close();
# get the Azure CosmosDB Primary Master Key
string tenantId = "";
string clientId = "the Azure AD application appid";
string subscriptionId = "the subscription id";
string rgName = "";
string accountName = "";
var creds = SdkContext.AzureCredentialsFactory.FromServicePrincipal(
clientId,
cert,
tenantId,
AzureEnvironment.AzureGlobalCloud
);
var azure = Azure.Configure()
.Authenticate(creds)
.WithSubscription(subscriptionId);
var keys = azure.CosmosDBAccounts.ListKeys(rgName, accountName);
Console.WriteLine(keys.PrimaryMasterKey);
Console.ReadLine();

How do I connect a server service to Dynamics Online

I am modifying an internal management application to connect to our online hosted Dynamics 2016 instance.
Following some online tutorials, I have been using an OrganizationServiceProxy out of Microsoft.Xrm.Sdk.Client from the SDK.
This seems to need a username and password to connect, which works fine, but I would like to connect in some way that doesn't require a particular user's account details. I don't think the OAuth examples I've seen are suitable, as there is no UI, and no actual person to show an OAuth request to.
public class DynamicsHelper
{
private OrganizationServiceProxy service;
public void Connect(string serviceUri, string username, string password)
{
var credentials = new ClientCredentials();
credentials.UserName.UserName = username;
credentials.UserName.Password = password;
var organizationUri = new Uri(serviceUri);
this.service = new OrganizationServiceProxy(organizationUri, null, credentials, null);
}
}
Is there a way to connect with an application token or API key?
I've found that to do this successfully, you'll need to setup all of the following:
Create an application registration in Azure AD:
grant it API permissions for Dynamics, specifically "Access Dynamics 365 as organization users"
give it a dummy web redirect URI such as http://localhost/auth
generate a client secret and save it for later
Create a user account in Azure AD and give it permissions to Dynamics.
Create an application user record in Dynamics with the same email as the non-interactive user account above.
Authenticate your application using the user account you've created.
For step 4, you'll want to open an new incognito window, construct a url using the following pattern and login using your user account credentials in step 2:
https://login.microsoftonline.com/<your aad tenant id>/oauth2/authorize?client_id=<client id>&response_type=code&redirect_uri=<redirect uri from step 1>&response_mode=query&resource=https://<organization name>.<region>.dynamics.com&state=<random value>
When this is done, you should see that your Dynamics application user has an Application ID and Application ID URI.
Now with your ClientId and ClientSecret, along with a few other organization specific variables, you can authenticate with Azure Active Directory (AAD) to acquire an oauth token and construct an OrganizationWebProxyClient. I've never found a complete code example of doing this, but I have developed the following for my own purposes. Note that the token you acquire has an expiry of 1 hr.
internal class ExampleClientProvider
{
// Relevant nuget packages:
// <package id="Microsoft.CrmSdk.CoreAssemblies" version="9.0.2.9" targetFramework="net472" />
// <package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="4.5.1" targetFramework="net461" />
// Relevant imports:
// using Microsoft.IdentityModel.Clients.ActiveDirectory;
// using Microsoft.Crm.Sdk.Messages;
// using Microsoft.Xrm.Sdk;
// using Microsoft.Xrm.Sdk.Client;
// using Microsoft.Xrm.Sdk.WebServiceClient;
private const string TenantId = "<your aad tenant id>"; // from your app registration overview "Directory (tenant) ID"
private const string ClientId = "<your client id>"; // from your app registration overview "Application (client) ID"
private const string ClientSecret = "<your client secret>"; // secret generated in step 1
private const string LoginUrl = "https://login.microsoftonline.com"; // aad login url
private const string OrganizationName = "<your organization name>"; // check your dynamics login url, e.g. https://<organization>.<region>.dynamics.com
private const string OrganizationRegion = "<your organization region>"; // might be crm for north america, check your dynamics login url
private string GetServiceUrl()
{
return $"{GetResourceUrl()}/XRMServices/2011/Organization.svc/web";
}
private string GetResourceUrl()
{
return $"https://{OrganizationName}.api.{OrganizationRegion}.dynamics.com";
}
private string GetAuthorityUrl()
{
return $"{LoginUrl}/{TenantId}";
}
public async Task<OrganizationWebProxyClient> CreateClient()
{
var context = new AuthenticationContext(GetAuthorityUrl(), false);
var token = await context.AcquireTokenAsync(GetResourceUrl(), new ClientCredential(ClientId, ClientSecret));
return new OrganizationWebProxyClient(new Uri(GetServiceUrl()), true)
{
HeaderToken = token.AccessToken,
SdkClientVersion = "9.1"
};
}
public async Task<OrganizationServiceContext> CreateContext()
{
var client = await CreateClient();
return new OrganizationServiceContext(client);
}
public async Task TestApiCall()
{
var context = await CreateContext();
// send a test request to verify authentication is working
var response = (WhoAmIResponse) context.Execute(new WhoAmIRequest());
}
}
With Microsoft Dynamics CRM Online or internet facing deployments
When you use the Web API for CRM Online or an on-premises Internet-facing deployment (IFD)
you must use OAuth as described in Connect to Microsoft Dynamics CRM web services using OAuth.
Before you can use OAuth authentication to connect with the CRM web services,
your application must first be registered with Microsoft Azure Active Directory.
Azure Active Directory is used to verify that your application is permitted access to the business data stored in a CRM tenant.
// TODO Substitute your correct CRM root service address,
string resource = "https://mydomain.crm.dynamics.com";
// TODO Substitute your app registration values that can be obtained after you
// register the app in Active Directory on the Microsoft Azure portal.
string clientId = "e5cf0024-a66a-4f16-85ce-99ba97a24bb2";
string redirectUrl = "http://localhost/SdkSample";
// Authenticate the registered application with Azure Active Directory.
AuthenticationContext authContext =
new AuthenticationContext("https://login.windows.net/common", false);
AuthenticationResult result =
authContext.AcquireToken(resource, clientId, new Uri(redirectUrl));
P.S: Concerning your method, it is a best practice to not to store the password as clear text, crypt it, or encrypt the configuration sections for maximum security.
See walkhrough here
Hope this helps :)
If I understand your question correctly, you want to connect to Dynamics 2016 (Dynamics 365) through a Registerd Azure Application with ClientId and Secret, instead of Username and Password. If this is correct, yes this is possible with the OrganizationWebProxyClient . You can even use strongly types assemblies.
var organizationWebProxyClient = new OrganizationWebProxyClient(GetServiceUrl(), true);
organizationWebProxyClient.HeaderToken = authToken.AccessToken;
OrganizationRequest request = new OrganizationRequest()
{
RequestName = "WhoAmI"
};
WhoAmIResponse response = organizationWebProxyClient.Execute(new WhoAmIRequest()) as WhoAmIResponse;
Console.WriteLine(response.UserId);
Contact contact = new Contact();
contact.EMailAddress1 = "jennie.whiten#mycompany.com";
contact.FirstName = "Jennie";
contact.LastName = "White";
contact.Id = Guid.NewGuid();
organizationWebProxyClient.Create(contact);
To get the AccessToken, please refer to the following post Connect to Dynamics CRM WebApi from Console Application.
Replace line 66 (full source code)
authToken = await authContext.AcquireTokenAsync(resourceUrl, clientId, new Uri(redirectUrl), new PlatformParameters(PromptBehavior.Never));
with
authToken = await authContext.AcquireTokenAsync( resourceUrl, new ClientCredential(clientId, secret));
You can also check the following Link Authenticate Azure Function App to connect to Dynamics 365 CRM online that describes how to secure your credentials using the Azure Key Vault.

Categories

Resources