Azure web jobs want me to set AzureWebJobsDashboard and AzureWebJobsStorage properties on startup but, i do not want to expose AccountKey in code.
When I try to use SAS Token, JobHostConfiguration class trying to parse SAS Token and throws exception
var config = new JobHostConfiguration();
config.DashboardConnectionString = ConfigurationManager.GetSetting(KeyVaultSecrets.StorageReadWriteConnectionString);
config.StorageConnectionString = ConfigurationManager.GetSetting(KeyVaultSecrets.StorageReadWriteConnectionString);
Exception
System.InvalidOperationException occurred
HResult=0x80131509
Message=Failed to validate Microsoft Azure WebJobs SDK Dashboard connection string. The Microsoft Azure Storage account connection string is not formatted correctly. Please visit http://msdn.microsoft.com/en-us/library/windowsazure/ee758697.aspx for details about configuring Microsoft Azure Storage connection strings.
Source=
StackTrace:
at Microsoft.Azure.WebJobs.Host.Executors.StorageAccountParser.ParseAccount(String connectionString, String connectionStringName, IServiceProvider services)
at Microsoft.Azure.WebJobs.Host.Executors.DefaultStorageAccountProvider.set_DashboardConnectionString(String value)
Is there any way to use SAS Token while creating configuration for JobHostConfiguration?
SAS tokens are not yet supported here. However, you don't need to put the connection strings in your code. You should be placing them in appsettings instead! Like so:
<configuration>
<connectionStrings>
<!-- The format of the connection string is "DefaultEndpointsProtocol=https;AccountName=NAME;AccountKey=KEY" -->
<!-- For local execution, the value can be set either in this config file or through environment variables -->
<add name="AzureWebJobsDashboard" connectionString="xxxxxxx" />
<add name="AzureWebJobsStorage" connectionString="yyyyyyyyy" />
</connectionStrings>
And furthermore, you can then set your appsettings directly on your site.
That said, in the latest nightly builds, we now have support for running [Timer] and [Singleton] on SAS connection strings; and you can disable logging by explicitly setting config.DashboardConnectionString to null. But we don't yet have support for binding [Blob] and other storage to SAS urls. See the unit test from this commit: https://github.com/Azure/azure-webjobs-sdk/blob/bd2d9ea34f13fc16569e8d8f80bafdb605eeb6f9/test/Microsoft.Azure.WebJobs.Host.EndToEndTests/InternalStorageTests.cs
We can use the SAS in a connection string. Because the SAS contains the information required to authenticate the request, a connection string with a SAS provides the protocol, the service endpoint, and the necessary credentials to access the resource according to that link
In this way, StorageAccountParser.ParseAccount(String connectionString) can parse the connection string
BlobEndpoint=https://storagesample.blob.core.windows.net;
SharedAccessSignature=sv=2015-04-05&sr=b&si=tutorial-policy-635959936145100803&sig=9aCzs76n0E7y5BpEi2GvsSv433BZa22leDOZXX%2BXXIU%3D
Related
Hi I posted this to the log4net user group but thought I'd post it here as well.
I'm working on a project that requires Azure MSI to connection from Azure PaaS to Azure SQL. Wondering if log4net’s ADOAppender supports this already connection mechanism already. From what I can tell it doesn't but thought I'd ask the community before extending log4net.
To support MSI apps can’t connect to a database with a connectionstring alone, they need to get an access token from Azure and then set the AccessToken property on the SqlConnection object. Like the code below is doing:
private static SqlConnection GetSqlConnection()
{
var sqlConnection = new SqlConnection(GetConnectionString());
if (sqlConnection.DataSource != "(localdb)\\MSSQLLocalDB")
sqlConnection.AccessToken = new AzureServiceTokenProvider()
.GetAccessTokenAsync("https://database.windows.net/").Result;
return sqlConnection;
}
This code is using two Microsoft nuget packages to get the access token.
Thanks!
As I understand, so long as I setup grants to KeyVault, my function should be able to read from it by using
#Microsoft.KeyVault(SecretUri=MYSECRETFULLURL), and I assume this would be transformed at run-time?
Any idea how I would debug this?
Currently, as thats getting to my function, is the above, with nothing transformed.
Running as system-managed.
If I debug, this is all I get:
However I can see my audit on azure key vault its being hit.
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log)
{
var accountToMoveFrom = System.Environment.GetEnvironmentVariable("accountToMonitor");
log.LogCritical(accountToMoveFrom);
var accessToken = System.Environment.GetEnvironmentVariable("accessToken");
}
Adding the final resolution:
Make sure you do not have the "authorized application" or applicationId settings configured.
From the documentation
Create an access policy in Key Vault for the application identity you created earlier. Enable the "Get" secret permission on this policy. Do not configure the "authorized application" or applicationId settings, as this is not compatible with a managed identity.
Note: Does your code actually work? Logging the value of a key is intercepted and is displayed as
#Microsoft.KeyVault(SecretUri=MYSECRETFULLURL)
in logs to avoid sensitive configuration from KeyVault ending up in
log files that may reach a wider audience.
It works fine as per the docs (extract below), also double check you have:
Managed Service Identity (MSI) configured on the function app
Restarted your function app after adding the function's app setting
The function's MSI is given access to to the relevant KeyVault, not to the Management Plane but on the Access Policies.
If you are running/debugging locally in Visual Studio, you need to give the account signed in to Visual Studio rights on the Key Vault since because it is the identity presented.
Sourcing Application Settings from Key Vault The Key Vault references feature makes it so that your app can work as if it were
using App Settings as they have been, meaning no code changes are
required. You can get all of the details from our Key Vault reference
documentation, but I’ll outline the basics here.
This feature requires a system-assigned managed identity for your app.
Later in this post I’ll be talking about user-assigned identities, but
we’re keeping these previews separate for now.
You’ll then need to configure an access policy on your Key Vault which
gives your application the GET permission for secrets. Learn how to
configure an access policy.
Lastly, set the value of any application setting to a reference of the
following format:
#Microsoft.KeyVault(SecretUri=secret_uri_with_version)
Where secret_uri_with_version is the full URI for a secret in Key
Vault. For example, this would be something like:
https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931
I am attempting to use Azure Key Vault from within my ASP.NET MVC Web Application, and I am following these instructions.
My Web.config looks like this (same as in the instructions):
<!-- ClientId and ClientSecret refer to the web application registration with Azure Active Directory -->
<add key="ClientId" value="clientid" />
<add key="ClientSecret" value="clientsecret" />
<!-- SecretUri is the URI for the secret in Azure Key Vault -->
<add key="SecretUri" value="secreturi" />
And my method to obtain the access token looks like this (same as instructions):
//add these using statements
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
using System.Web.Configuration;
//this is an optional property to hold the secret after it is retrieved
public static string EncryptSecret { get; set; }
//the method that will be provided to the KeyVaultClient
public static async Task<string> GetToken(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(WebConfigurationManager.AppSettings["ClientId"],
WebConfigurationManager.AppSettings["ClientSecret"]);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken;
}
I have placed my ClientId, ClientSecret, and SecretUri into my web app's Application Settings, just like the screenshot shows in the instructions. Since I did this, I can expect (from the instructions):
If you have an Azure Web App, you can now add the actual values for the AppSettings in the Azure portal. By doing this, the actual values will not be in the web.config but protected via the Portal where you have separate access control capabilities. These values will be substituted for the values that you entered in your web.config. Make sure that the names are the same.
However, when I run the method above, the value for WebConfigurationManager.AppSettings["ClientId"] resolves to clientid which is the dummy value, and likewise for ClientSecret. My understanding is that the method is supposed to reach out to the web app in the Azure Portal and substitute the values. What am I missing? Why aren't the values being substituted?
Edit: Also, it may be important that I'm using Azure Active Directory B2C instead of Azure Active Directory.
When you run or debug application from your local environment the application picks values from web.config and so you are seeing dummy values on your web page. Your application will pick values from Azure App settings when you deploy your application to Azure. Also, you need to keep Key Name same in web.config as well as in the Azure app setting. Hope this helps.
We have web application deploy on Azure App Service. Our database is also on Azure which is configured to use AAD authentication (We have assigned AAD Admin).
We are using below connection string in web app to connect to this server and database using below connections string.
Data Source=xxxxxxx.database.windows.net;Initial
Catalog=xxxxxxx;Persist Security Info=False;Authentication=Active
Directory Integrated
Please note: This connection string is working fine when using thru local system. But getting below error when we use this conn string in Azure App Service:
Failed to authenticate the user NT Authority\Anonymous Logon in Active
Directory (Authentication=ActiveDirectoryIntegrated). Error code
0x4BC; state 10 The format of the specified domain name is invalid
According to your description, I found you used the Active Directory integrated authentication.
To use integrated Windows authentication, your domain’s Active Directory must be federated with Azure Active Directory. Your client application (or a service) connecting to the database must be running on a domain-joined machine under a user’s domain credentials
If you published the web app to Azure, Azure's web app server will not be in your domain’s Active Directory. So the SQL server will not pass the auth.
I suggest you could try to use Active Directory password authentication instead of the Active Directory integrated authentication.
Replace the connection string as below use azure AD user name and password. It will work well.
Server=tcp:brandotest.database.windows.net,1433;Initial Catalog=bradnotestsql;Persist Security Info=False;User ID={your_username};Password={your_password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication="Active Directory Password";
Since the accepted answers are a bit dated, if you are out here in 2020 or later, the correct way for setting up integrated authentication is as follows:
(excerpted from here, the asp.net standard implementation)
https://learn.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi
add the Microsoft.Azure.Services.AppAuthentication nuget package.
modify your web.config by adding: (in configSections)
<section name="SqlAuthenticationProviders" type="System.Data.SqlClient.SqlAuthenticationProviderConfigurationSection, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
(and then)
<SqlAuthenticationProviders>
<providers>
<add name="Active Directory Interactive" type="Microsoft.Azure.Services.AppAuthentication.SqlAppAuthenticationProvider, Microsoft.Azure.Services.AppAuthentication" />
</providers>
</SqlAuthenticationProviders>
It's important to pay attention to the name you use there. Then... your connection string will look like:
<add name="MyEntities" connectionString="metadata=res://*/Data.MyDB.csdl|res://*/Data.MyDB.ssdl|res://*/Data.MyDB.msl;provider=System.Data.SqlClient;provider connection string="server=tcp:MyDB.database.windows.net;database=MyDB;UID=AnyString;Authentication=Active Directory Interactive;"" providerName="System.Data.EntityClient" />
The important notes are that the name you specify in the SqlAuthenticationProviders section must be the exact same name you use in the connection string for Authentication.
The other important note is that, coming from your old connection strings, you have to change Data Source to be Server, and Initial Catalog to be Database. UID=AnyString is necessary, or an exception is thrown.
Failure to follow these steps exactly will net you a lovely error:
System.Data.Entity.Core.EntityException: The underlying provider failed on Open. ---> System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> AdalException: The format of the specified domain name is invalid.\r\n at ADALNativeWrapper.ADALGetAccessToken(String username, IntPtr password, String stsURL, String servicePrincipalName, ValueType correlationId, String clientId, Boolean* fWindowsIntegrated, Int64& fileTime)\r\n at System.Data.SqlClient.ActiveDirectoryNativeAuthenticationProvider.<>c__DisplayClass2_0.b__0()\r\n at System.Threading.Tasks.Task`1.InnerInvoke()\r\n at System.Threading.Tasks.Task.Execute()\r\n --- End of inner exception stack trace
At the first the error doesn't make sense, but once you see that the parameters were renamed from Data Source to Server, it does make sense.
Maybe all you need to use is token (certificate) authentication as explained on below resource:
https://github.com/Microsoft/sql-server-samples/tree/master/samples/features/security/azure-active-directory-auth/token
Try to register your application with Azure Active Directory as explained on that resource.
Hope this helps.
I am coding an integration that has to call Sharepoint-online API's. My integration is not a webapp and has to work without a user present.
As I understand it I need two setup steps:
1. User has to log in to Azure and set up an application and obtain a client ID.
2. I have to call a service with client ID and username and password I will then obtain an Access Token, Refresh Token and ID Token
Once the two setup steps are complete I then can call the service using the access token, but sometimes this will expire and I need to use the refresh token to get a new one.
Step 2 seems odd to me. Why isn't there a user interface where a user can log in and obtain the Access Refresh and ID tokens? Has someone built a utility website that just does this, or have I mis-understood something?
Thanks
Robert
The recommended OAuth flow for service and daemons apps is the Client Credential Flow (in that flow, there no refresh tokens involved; a client ID and a client secret is used to obtain an access token which eventually expires and then you need to get a new access token using the same client ID and secret). In the case of SharePoint Online, you have 2 options for this scenario:
SharePoint Online + Azure Access Control Service (ACS) integration. Details here. In short, you create a service principal (add in only policy) for instance at the site collection level - follow the "Creating the AppPrincipal" section in the blog I linked for this. Then you need to assign the specific permissions your app will need, in the application manifest. See a sample for that in the "Giving the App Principal Permissions" sections - again, you should first define what permissions your app needs. Then, you can use the service principal from a console application:
Program.cs
static void Main(string[] args)
{
Uri siteUri = new Uri("https://tenant.sharepoint.com/teams/test");
//Get the realm for the URL
string realm = TokenHelper.GetRealmFromTargetUrl(siteUri);
//Get the access token for the URL.
// Requires this app to be registered with the tenant
string accessToken = TokenHelper.GetAppOnlyAccessToken(
TokenHelper.SharePointPrincipal,
siteUri.Authority, realm).AccessToken;
HttpWebRequest endpointRequest =
(HttpWebRequest)HttpWebRequest.Create(
"https://tenant.sharepoint.com/teams/test/_api/web/lists/GetByTitle('Documents')/items");
endpointRequest.Method = "GET";
endpointRequest.Accept = "application/json;odata=verbose";
endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken);
HttpWebResponse endpointResponse =
(HttpWebResponse)endpointRequest.GetResponse();
}
}
app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<appSettings>
<add key="ClientId" value="65e674ca-3827-4134-852b-1196ff935e08"/>
<add key="ClientSecret" value="xxxxxxx"/>
</appSettings>
</configuration>
SharePoint Online + Azure Active Directory (AAD) integration. Details here. In that link you will find a sample code. The difference between the first approach is that in this one you are not using ACS but AAD. The permission that the app needs is defined in AAD - as of today, as far as I know, the application permissions that you can define in AAD are not as granular as the ones you can define via ACS - i.e. with ACS you can define an app at the site collection level, with AAD you can't the app will have tenant wide permissions (i.e. all site collections)