Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure WebJob Host/Runtime doesn't support Managed Identity (using the Azure WebJob SDK 3.x) #3069

Open
gautammoulik opened this issue Mar 29, 2024 · 4 comments

Comments

@gautammoulik
Copy link

gautammoulik commented Mar 29, 2024

Azure WebJob runs 2 types of code - a) the WebJob host/Runtime and b) Application code (write by the developers) - For the second part it's very easy to connect to a Azure Storage using a Managed Identity.
However, for the first part which is WebJob Host - it communicate with the Azure Storage for various internal reasons, any Storage related communication details are controlled thru configuration like - "AzureWebJobStorage" and "AzureWebJobsDashboard".
None of these configuration setting allow to configure a Managed Identity, neither there is any documentation about the alternative configurations can be used to force the WebJob host/Runtime use Managed Identity while communicating with the Azure Storage.

Repro steps

Provide the steps required to reproduce the problem

  1. Step A
    Create a WebJob with a timer triggered function.
    Remove the AzureWebJobStorage configuration (or keep the value as "" ).

Note - Although some related documentation about Azure Function claims about Azure Function works with the below configurations -
AzureWebJobsStorage__accountName
AzureWebJobsStorage__Credential

Adding the above settings didn't do any help.

  1. Step B

Expected behavior

The WebJob function should be running when the timer triggered( or the configured time expired).

Actual behavior

The WebJob throws an error on the Timer expired event -
Microsoft.Azure.WebJobs.Host.Listeners.FunctionListenerException: The listener for function 'TokenIngestAgentQueueFunction.ProcessIngestJobRequestFromQueueAsync' was unable to start. ---> System.ArgumentNullException: Value cannot be null.
Parameter name: connectionString
at Microsoft.Azure.Storage.CloudStorageAccount.Parse(String connectionString)
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.get_TimerStatusDirectory() in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 86
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusBlobReference(String timerName) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 144
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.d__10.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.WebJobs.Extensions.Timers.Listeners.TimerListener.d__27.MoveNext() in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Listener\TimerListener.cs:line 99
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.WebJobs.Host.Listeners.SingletonListener.d__13.MoveNext() in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\Singleton\SingletonListener.cs:line 72
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.WebJobs.Host.Listeners.FunctionListener.d__13.MoveNext() in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\Listeners\FunctionListener.cs:line 69
--- End of inner exception stack trace ---
at Microsoft.Azure.WebJobs.Host.RecoverableException.TryRecover(ILogger logger) in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\Exceptions\RecoverableException.cs:line 81
at Microsoft.Azure.WebJobs.Host.Listeners.FunctionListener.d__13.MoveNext() in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\Listeners\FunctionListener.cs:line 81
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.WebJobs.Host.Listeners.FunctionListener.d__12.MoveNext() in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\Listeners\FunctionListener.cs:line 61
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.WebJobs.Host.Listeners.CompositeListener.d__4.MoveNext() in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\Listeners\CompositeListener.cs:line 39
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.WebJobs.Host.Listeners.ListenerFactoryListener.d__8.MoveNext() in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\Listeners\ListenerFactoryListener.cs:line 47
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.WebJobs.Host.Listeners.ShutdownListener.d__5.MoveNext() in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\Listeners\ShutdownListener.cs:line 29
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.WebJobs.JobHost.d__23.MoveNext() in D:\a_work\1\s\src\Microsoft.Azure.WebJobs.Host\JobHost.cs:line 101
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Extensions.Hosting.Internal.Host.d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at Microsoft.SupplyChain.TokenIngest.Agent.Program.Main() in C:__w\1\s\src\TokenIngestAgent\Program.cs:line 142

Known workarounds

N/A

Related information

Provide any related information

  • Packages
    Microsoft.Azure.WebJobs - 3.0.39
    Microsoft.Azure.WebJobs.Extensions - 4.0.1
    Microsoft.Azure.WebJobs.Extensions.Storage - 4.0.2

  • Links to source

@mattchenderson
Copy link

Identity is only supported starting with version 5.x of the storage extension. You can see in the stack trace that Microsoft.Azure.Storage is being used, which is the older library. You instead want to be using the newer Azure.Storage packages. This transition is the key difference between 4.x and 5.x, and support for identity is one of the main features this enables.

When navigating the difference in the types used, the Storage SDK team has provided the following transition guide, which may help: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/AzureStorageNetMigrationV12.md

@gautammoulik
Copy link
Author

gautammoulik commented May 6, 2024

@mattchenderson -
Thanks for your response.
To be clear - the business code (aka the code I wrote to run inside the WebJob) can connect to the Azure Storage using the Managed Identity without any issues. I have explicitly added a reference to Azure.Storage.Blob package only.

The issue is related to the WebJob infra code (which comes along with the Microsoft.Azure.WebJobs and it's extension packages ), for example a Timer trigger based Web job needs to communicate with Storage Account.
It seems the WebJob infra doesn't know/support how to connect to a Storage using a Managed Identity.

I am using the below WebJob packages -
Microsoft.Azure.WebJobs - 3.0.39
Microsoft.Azure.WebJobs.Extensions - 4.0.1
Microsoft.Azure.WebJobs.Extensions.Storage - 4.0.2

Can you please share me a sample project which runs a WebJob (with timer trigger) and the WebJob infra code connecting to Storage account (to acquire a Lock while starting) using the Managed Identity.

Thanks !!
Gautam

@eureka-gh

This comment was marked as outdated.

@eureka-gh
Copy link

eureka-gh commented May 13, 2024

I am having the same issue in my webjob program where timer trigger requires StorageAccount connection string in AzureWebJobsStorage which I want to get rid of with MSI.

According to this microsoft learn link:

The Azure Functions host uses the storage connection set in AzureWebJobsStorage to enable core behaviors such as coordinating singleton execution of timer triggers and default app key storage. This connection can also be configured to use an identity.
The solution is to use "AzureWebJobsStorage__accountName" with proper roles (AKA, RBAC).

I did the same setting in my WebJob project (Microsoft.Azure.WebJobs" Version="3.0.33", "Microsoft.Azure.WebJobs.Extensions" Version="3.0.6"), it doesn't work as @gautammoulik mentioned. The error message below is to do with null connection string (ALthough, I've set Environment.SetEnvironmentVariable("AzureWebJobsStorage__accountName", storageAcctOptions.AccountName) in Program.cs/GetJobHost() )

The listener for function 'CustomSchedulerFunction.CustomSchedulerHandler' was unable to start.
Microsoft.Azure.WebJobs.Host.Listeners.FunctionListenerException: The listener for function 'CustomSchedulerFunction.CustomSchedulerHandler' was unable to start.
---> System.ArgumentNullException: Value cannot be null. (Parameter 'connectionString')
at Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse(String connectionString)
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.get_TimerStatusDirectory() in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 77
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusBlobReference(String timerName) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 144
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusAsync(String timerName) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 93
at Microsoft.Azure.WebJobs.Extensions.Timers.Listeners.TimerListener.StartAsync(CancellationToken cancellationToken) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Listener\TimerListener.cs:line 99
at Microsoft.Azure.WebJobs.Host.Listeners.SingletonListener.StartAsync(CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Singleton\SingletonListener.cs:line 72
at Microsoft.Azure.WebJobs.Host.Listeners.FunctionListener.StartAsync(CancellationToken cancellationToken, Boolean allowRetry) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Listeners\FunctionListener.cs:line 69
--- End of inner exception stack trace ---

@gautammoulik, Luckly, after a couple of attempts, it turns out the "AzureWebJobsStorage__accountName" cannot be interpreted by WebJobs.Extension <= 3.0.33. If I upgrade the Microsoft.Azure.WebJobs.Extensions to "5.0.0" as of 05/12/2024. ("Microsoft.Azure.WebJobs" to 3.0.39 seems an optional), MSI works for me.

Although, I still don't know how to use a user managed identity or AAD App identity. I am guessing you need to Add Microsoft.Azure.WebJobs.Extensions.Timers.Storage, and then update TimerOptions like: webJobBuilder.AddTimers(/* options => TimerOptions.AddBlobServiceClient() */).

I don't have time to test, but I feel it's in the right track though. Hope it helps.

PS. my project is based on .NetCore 3.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants