I have an azure function that uses the Azure context. When I execute my function from visual studio 2019 on my machine, it executes correctly. However when I publish this to my Azure account, I get an error that the my.azureauth file cannot be found.
Could not find file 'D:\Program Files (x86)\SiteExtensions\Functions\2.0.12950\32bit\my.azureauth'
The code that is used:
var authFilePath = "my.azureauth";
Console.WriteLine($"Authenticating with Azure using credentials in file at {authFilePath}");
azure = Azure.Authenticate(authFilePath).WithDefaultSubscription();
sub = azure.GetCurrentSubscription();
Console.WriteLine($"Authenticated with subscription '{sub.DisplayName}' (ID: {sub.SubscriptionId})");
This is code that I found on one of the Microsoft tutorials. I have set my my.azureauth file to "Copy Always".
Could anyone point my in the right direction?
You are get this file path because the Directory.GetCurrentDirectory() would return D:\Program Files (x86)\SiteExtensions\Functions\2.0.12950\32bit instead of D:\home\site\wwwroot\ or D:\home\site\wwwroot\FunctionName.
And if you want to get the wwwroot folder or the function app directory you should use ExecutionContext. Further more information you could refer to this wiki doc.
So the right file path should be context.FunctionDirectory+"\my.azureauth" or context.FunctionAppDirectory+"\my.azureauth", which one to use depends on where your file is stored.
I have found that Kudu is extremely useful in seeing what has been deployed to Azure.
Navigate to your function in the Azure portal.
The instructions here will help get to the kudu console.
https://www.gslab.com/blogs/kudu-azure-web-app
From there you can browse the files which have been deployed into your function's file system.
If you add " , ExecutionContext context)" at the end of the function's run entry point, you can then get the folder which your function is running from with "var path = context.FunctionAppDirectory;
PS apologies for any formatting I am editing this on my phone.
Welcome to Stackoverflow.
Firstly, I'd recommend strongly against using file-based authentication as shown in your question.
From notes:
Note, file-based authentication is an experimental feature that may or may not be available in later releases. The file format it relies on is subject to change as well.
Instead, I would personally store the connection string details (AzureCredentials) in the config file (Web/SiteSettings) and use the provided constructor...
Again, the below are taken from the documentation notes:
Similarly to the file-based approach, this method requires a service principal registration, but instead of storing the credentials in a local file, the required inputs can be supplied directly via an instance of the AzureCredentials class:
var creds = new AzureCredentialsFactory().FromServicePrincipal(client, key, tenant, AzureEnvironment.AzureGlobalCloud);
var azure = Azure.Authenticate(creds).WithSubscription(subscriptionId);
or
var creds = new AzureCredentialsFactory().FromServicePrincipal(client, pfxCertificatePath, password, tenant, AzureEnvironment.AzureGlobalCloud);
var azure = Azure.Authenticate(creds).WithSubscription(subscriptionId);
where client, tenant, subscriptionId, and key or pfxCertificatePath and password are strings with the required pieces of information about your service principal and subscription. The last parameter, AzureEnvironment.AzureGlobalCloud represents the Azure worldwide public cloud. You can use a different value out of the currently supported alternatives in the AzureEnvironment enum.
The first example is most likely the one you should be looking at.
The notes I got this information from can be accessed here.
If you have some problems with AAD, these screenshots may help you.
Client ID:
Key:
Please note that the Key value can only be copied when it is created, after which it will be hidden.
Hope this helps you get started with AAD quickly.
Related
I'm attempting to connect to an Azure Key Vault instance from a .NET 4.7 application running locally under IIS and the debugger (Visual Studio 2022 17.4.4) but am encountering the below exception(s) from the Azure.Identity package when it attempts to retrieve a token to authenticate to Azure when calling to perform a KeyVault action such as GetSecretAsync().
DefaultAzureCredential failed to retrieve a token from the included
credentials. See the troubleshooting guide for more information.
https://aka.ms/azsdk/net/identity/defaultazurecredential/troubleshoot
ManagedIdentityCredential authentication unavailable. Multiple attempts failed to obtain a token from the managed identity endpoint.
Visual Studio Token provider can't be accessed at C:\WINDOWS\system32\config\systemprofile\AppData\Local.IdentityService\AzureServiceAuth\tokenprovider.json
I need to connect to the KeyVault instance via a User Assigned Managed Identity in cloud environments such as production, whereas in development environments, we therefore need to connect via the developer's Visual Studio account to authenticate them to access the service, similarly. Perhaps I have misunderstood, but I believed this is possible via the DefaultAzureCredential option, which will try various methods of authentication in order (such as environment variables, managed identities, then Visual Studio credentials, etc) until one succeeds.
When inspecting the inner exception(s) relating to the Visual Studio Credentials flow, I see the System.Exception {System.IO.DirectoryNotFoundException} exception message states...
"Could not find a part of the path 'C:\WINDOWS\system32\config\systemprofile\AppData\Local.IdentityService\AzureServiceAuth\tokenprovider.json'.
Previously, this message had stated the below message (which I understand to be the more recent location for this file), until I attempted to run under Visual Studio 2019 for comparison, at which point, it changed to the above message.
"Could not find a part of the path C:\Users[AppPoolName]\AppData\Local.IdentityService\AzureServiceAuth\tokenProvider.json".
At first, I noticed the path didn't exist from .IdentityService onward, and so followed the suggestion on this MSFT forum post to restore the AppAuthentification extension from VS2019 into VS2022's configuration to restore the C:\Users\<AppPoolName>\AppData\Local\.IdentityService\AzureServiceAuth\tokenprovider.json file and providers the TokenProviders as a path to C:\Program Files (x86)\Microsoft Visual Studio\<version>\Enterprise\Common7\IDE\Extensions\<random dir name>\TokenService\Microsoft.Asal.TokenService.exe. On the next build, I noticed .IdentityService had been created, but not the proceeding directory or file.
I then tried logging out and into Visual Studio a number of times, but this did not seem to create the remaining missing directory and file. Creating the directory and file manually of course resolve the System.IO.DirectoryNotFoundException, but the error message then informs me that the file schema is incorrect. I'm unable to find an example with the correct schema and values.
In terms of client configuration options, I've been explicitly limiting the modes of authentication flow to just ManagedIdentity and VisualStudioCredential for simplicity after noticing other methods (e.g. AzureCLI and Azure PowerShell Module` also failed, despite being logged in to them).
_client = new SecretClient(new Uri(options.KeyVaultUri), new DefaultAzureCredential(
new DefaultAzureCredentialOptions
{
ExcludeManagedIdentityCredential = false,
ExcludeVisualStudioCredential = false,
ExcludeInteractiveBrowserCredential = true,
ExcludeAzurePowerShellCredential = true,
ExcludeAzureCliCredential = true,
ExcludeEnvironmentCredential = true,
ExcludeVisualStudioCodeCredential = true,
ExcludeSharedTokenCacheCredential = true,
ManagedIdentityClientId = options.ManagementIdentityClientId
}
));
I've also tried the suggestions on Azure SDK GitHub Issue #4590 of settings setProfileEnvironment and loadUserProfile to true in case it's an IIS permissions issue, but this made no difference - the same errors continue.
Finally, the only other reference I've found to the tokenProvider.json file is in Microsoft's documentation for App Authentication, but the re-authenticate button doesn't exist in the Tools > Options > Azure Service Authentication window as suggested.
"If you run into problems using Visual Studio, such as errors that
involve the token provider file, carefully review the preceding steps.
You may need to reauthenticate your developer token. To do so, select
Tools > Options, and then select Azure Service Authentication. Look
for a Re-authenticate link under the selected account. Select it to
authenticate."
As I'm able to locate C:\Program Files (x86)\Microsoft Visual Studio\<version>\Enterprise\Common7\IDE\Extensions\<random dir name>\TokenService\Microsoft.Asal.TokenService.exe and its related configuration file, I suspect it's the missing tokenProvider.json file that's the issue, but I'm not aware of what is responsible for creating that, nor what it should contain.
Any insight or pointers would be appreciated.
Notable packages and their versions in use:
Azure.Identity # v1.8.0
Azure.Security.KeyVault.Secrets # v4.4.0
Edit (1)
As one might expect, I'm able to configure an alternative flow to work by granting an RBAC record upon the Key Vault for an Azure AD Application Registration and then using the ClientSecretCredential flow in place of the DefaultAzureCredentials flow (as below). But this doesn't solve the problem in the best way so I'd be interested if anyone can spot where I'm going wrong with the DefaultAzureCredentials flow, if at all.
_client = new SecretClient(new Uri(options.KeyVaultUri),
new ClientSecretCredential(options.TenantId, options.ClientId, options.Secret)
);
Having found this question on SO, I found that tokenProvider.json existed in the same directory under C:\Users\<local user account> (via %LOCALAPPDATA%.IdentityService\AzureServiceAuth\) and was able to analyse it for reference and duplicate it to the IIS.
As suggested by Johnny5, it seems VisualStudioCredentials executes as the signed-in domain user, but IIS is running as the ApplicationPoolIdentity, hence it doesn't access the file under the domain user location and doesn't create one as it's not signed into Visual Studio. After a little research on how to alter this, I was able to set the IIS Application Pool identity as my domain user which matched the signed-in Visual Studio account.
To do this, follow these steps
Open IIS Manager > Go to Application Pools
Right-click the relevant pool, then click Advanced Settings...
Click the 3-dot button next to the Identity settings (likely ApplicationPoolIdentity)
Select Custom Account and enter your credentials (including any domain prefix - e.g. DOMAIN\MyUser)
If you aren't sure of your domain, open a command prompt and enter echo %USERDOMAIN% to find it.
I then set the SecretClient authentication flow back to utilise DefaultAzureCredential like so, re-tested locally, and success - secrets retrieved.
_client = new SecretClient(new Uri(options.KeyVaultUri), new DefaultAzureCredential(
new DefaultAzureCredentialOptions()
{
ExcludeEnvironmentCredential = true,
ExcludeVisualStudioCodeCredential = true,
ExcludeAzureCliCredential = true,
ExcludeAzurePowerShellCredential = true,
ExcludeSharedTokenCacheCredential = true,
ExcludeInteractiveBrowserCredential = true,
ExcludeManagedIdentityCredential = false,
ExcludeVisualStudioCredential = false,
ManagedIdentityClientId = options.ManagementIdentityClientId,
}));
I try to generate key from this code
CngKey key = CngKey.Import(Convert.FromBase64String(privateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
it works fine locally but when I deploy in my Azure app service.
it gives me this error:
System.Security.Cryptography.CryptographicException: The system cannot find the file specified.
at System.Security.Cryptography.NCryptNative.ImportKey(SafeNCryptProviderHandle provider, Byte[] keyBlob, String format)
at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, String curveName, CngKeyBlobFormat format, CngProvider provider)
I add WEBSITE_LOAD_USER_PROFILE In Configuration with value '1' but it didn't make any difference.
Thanks
If WEBSITE_LOAD_USER_PROFILE = 1 does not work for you. Here is a workaround for this same issue:
Open your Azure App Service (Azure Website) blade in portal.azure.com
Go to the Application settings page
Scroll to App settings
Add a new entry key: WEBSITE_LOAD_CERTIFICATES, and provide a dummy (fake, made-up, randomly-generated) value for it.
Check out the below links for the similar issue for reference:
X509Certificate2 on Azure App Services (Azure Websites) since mid-2017?
GH issue #16
This issue is not only on Azure, I had the same issue on my VPS as well and this answer save my life:
X509Certificate Constructor Exception
Cheers,
Nick
I upgraded the plan service from free to basic with adding WEBSITE_LOAD_USER_PROFILE = 1 in Azure Configuration.
The issue was when the app service was free it use a shared VM but when upgrading my app service pricing into basic it use a private VM.
I have spent around 4 days looking for this also. I have found out another reason. When you run VS as administrator, then you automatically get the privileges to read something called "cert user store".
However if you run it on another machine or some hosting, where you do not have full privileges, then you might run into this issue as well.
Hope this helps as well.
I have been using c# code to get blob items for the past few days, however, with no changes to the way the program gets the blob data, it stopped working. I run into the same error every time I run now:
"EnvironmentCredential authentication unavailable. Environment variables are not fully configured"
Here is the code I am using to connect to Azure:
Uri accountUri = new Uri(mystorageurl);
BlobServiceClient client = new BlobServiceClient(accountUri, new DefaultAzureCredential(true));
BlobContainerClient container = client.GetBlobContainerClient(blobname);
BlobClient bundle = container.GetBlobClient(itemname);
What I've been confused by is that if I run this same code in a separate vs solution, I get no error getting the files from Azure. I've also tried sending the same solution that's getting the error to another person and they were able to run it without issue. I know it isn't an issue with environment variables, since it used to work up until now and they haven't been modified in any way.
This unresolved issue on github is most similar to what I've encountered:
https://github.com/Azure/azure-sdk-for-net/issues/16079
It worked fine when you never set Environment variables, it means that you didn't use EnvironmentCredential. The DefaultAzureCredential will attempt to authenticate via the following mechanisms in order, and Environment is the first one.
If you just use Environment to authenticate, it's better to use EnvironmentCredential instead of DefaultAzureCredential. And it's necessary to set the following variables.
AZURE_CLIENT_ID: id of an Azure Active Directory application
AZURE_TENANT_ID: id of the application's Azure Active Directory tenant
AZURE_CLIENT_SECRET: one of the application's client secrets
We have a mail template in ~/Content/EmailTemplate/template.cshtml. But whenever we do the following:
var path="D:/site/wwwroot/Content/EmailTemplate/template.cshtml"
File.Exist(path)
File.Exist(path) returns false. While debugging in local source it works fine. It returns false in azure web app only. I have checked the file already exists there.
If you move the above code to another environment it will break.
I would suggest that you use map path as this will assure that you can move from hosting environment to hosting environment, including your local development environment.
string path = Server.MapPath("~/path/tofile");
You will also have higher confidence that you are targeting the file correctly.
For Azure WebApp,
D:\home is shared for us and we could read or write file in this path. More detail about Home directory access please refer to the WebApp Sandbox. File structure on Azure please refer to another document. We could browse it from Kudu (http://yourwebsite.scm.azurewebsites.net/) tool. In your case, we also could use the following code in the Azure WebApp.
string path = Environment.GetEnvironmentVariable("HOME") + #"\site\wwwroot\Content\EmailTemplate\template.cshtml"
var rawData = Convert.FromBase64String(_signingKey);
var cng = CngKey.Import(rawData, CngKeyBlobFormat.Pkcs8PrivateBlob);
I use this code to extract key, from embedded base64 string.
It works fine when I test it locally but when I publish on azure I get following exception:
WindowsCryptographicException: The system cannot find the file specified
(once again I'm not reading from any file)
I need this to communicate with apple apns for push notifications, is there any workaround?
And this happens only on free service plan, if I switch to basic plan it's working.
I ran into the same error after publishing an existing application to Azure. In my case the problem was solved after I set WEBSITE_LOAD_USER_PROFILE = 1 in App Services / App Name / Application Settings.
Setting WEBSITE_LOAD_USER_PROFILE to equal 1 in the Azure App Service configuration definitely got my remote iOS notifications working. Using dotAPNS for C# .NET I also needed to omit apns.UseSandbox().
It seems that it causes by there is no certificate attached in your Azure Mobile App. If it is that case, we need to upload the "Development" or "Distribution" SSL certificate to the WebApp. More info about how to send push notifications to iOS App, please refer to the azure document.
I've had a similar error trying to construct a X509Certificate2 from a byte array - worked fine locally but once I deploy to Azure Web App, I got the same and VERY misleading file not found exception.
The real issue turned out to be that there was no user store associated with the web service account. You can also get a similar error if there are permission-related errors with accessing the certificate store on Windows.
In any case - In my scenario I fixed the problem by using MachineKeySet:
new X509Certificate2(certRawBytes, default(string), X509KeyStorageFlags.MachineKeySet);
So, in your scenario, try something like:
var keyParams = new CngKeyCreationParameters
{
KeyCreationOptions = CngKeyCreationOptions.MachineKey,
};
CngKey.Create(CngAlgorithm.Rsa, keyName, keyParams);
Note: You may have to set a few parameters to get the above working. The Import method doesn't seem to support MachineKey - but you should be able to achieve similar outcome by using the Create method.
To add to #strohmsn's answer, you can also set the App Service settings with this value directly within Visual Studio on the Publish page for web apps: Right click on web app and select Publish, then select App Service Settings, and you can add setting properties there: WEBSITE_LOAD_USER_PROFILE = 1 in this case. See screenshot:
For making it works, I needed TWO things in AzureWebApp..
So my code is :
//I load the PrivateKey here
ReadedByte = System.IO.File.ReadAllBytes(strPathPrivateKey);
//create the RSA thing
RSA rsa = System.Security.Cryptography.RSA.Create();
//import the key. It crashed HERE with the 'System cannot find file specified'
rsa.ImportPkcs8PrivateKey(source: ReadedByte,bytesRead: out int _);
It works perfectly locally. But, to make it WORK on Azure Web App, I had to have those TWO requirements :
1 - the WEBSITE_LOAD_USER_PROFILE = 1 spoken in the discussion above and below
2 - The App Service Plan must include "Custom domains / SSL" !
...so No 'F1 Share Infrastructure' nor 'D1 Share Infrastructure'. The lowest Service plan that worked for me was 'B1 - 100 Total Acu'.
Maybe I have something wrong somewhere else in my code, or my 'RSA' choice is bad..anyway...
It now works!