azure-sdk-for-net
azure-sdk-for-net copied to clipboard
[BUG] ArgumentException 'stream must be seekable' when calling ReplaceContentRunbookDraftAsync.
Library name and version
Azure.ResourceManager.Automation 1.0.0
Describe the bug
We are consistently observing an ArgumentException 'stream must be seekable' when calling ReplaceContentRunbookDraftAsync to upload a runbook to Azure Automation. The runbook content was converted to a buffer of UTF encoded bytes and which was then used to create a MemoryStream. We have confirmed that the stream that is being bassed to ReplaceContentRunbookDraftAsync is indeed seekable.
Expected behavior
The runbook content should be uploaded to Azure Automation without error.
Actual behavior
Fails with ArgumentException 'stream must be seekable'.
Reproduction Steps
public async Task UploadRunbookContent(ResourceIdentifier runbookIdentifier, string content) { var options = new InteractiveBrowserCredentialOptions { TenantId = "<Your Tenant ID>" ClientId = "<Your Cliend ID>" RedirectUri = new Uri("<Your Redirect URI>") };
var credential = new InteractiveBrowserCredential(options);
var armClient = new ArmClient(credential);
AutomationRunbookResource resource = armClient.GetAutomationRunbookResource(runbookIdentifier);
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
await resource.ReplaceContentRunbookDraftAsync(WaitUntil.Completed, stream);
}
}
Environment
No response
Here is the stack trace:
StackTrace " at Azure.Core.RequestContent.StreamContent..ctor(Stream stream)\r\n at Azure.ResourceManager.Automation.RunbookDraftRestOperations.CreateReplaceContentRequest(String subscriptionId, String resourceGroupName, String automationAccountName, String runbookName, Stream runbookContent)\r\n at Azure.ResourceManager.Automation.AutomationRunbookResource.<ReplaceContentRunbookDraftAsync>d__28.MoveNext()\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at Kelverion.RunbookStudio.Cloud.Client.AutomationRunbookCollectionClient.<DoUpdateAsync>d__9.MoveNext() in C:\\Users\\DavidCader\\source\\repos\\app-automation-console\\Src\\Kelverion.RunbookStudio.Cloud\\Client\\AutomationRunbookCollectionClient.cs:line 134\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.ConfiguredTaskAwaitable
1.ConfiguredTaskAwaiter.GetResult()\r\n at Kelverion.RunbookStudio.Cloud.Client.AutomationCollectionClientBase`1.<UpdateAsync>d__13.MoveNext() in C:\Users\DavidCader\source\repos\app-automation-console\Src\Kelverion.RunbookStudio.Cloud\Client\AutomationCollectionClientBase.cs:line 121" string
I thought the problem might have been due to disposing the stream explicitly. However, when I updated the code so that the stream was not disposed the same exception was still observed. This is a major blocking issue for us.
Thank you for your feedback. Tagging and routing to the team member best able to assist. Please expect delayed responses due to the US holidays.
It looks like the stream is being disposed, but our code is not explicitly doing so. Investigating further, but now struggling with an Azure.Identity authentication failure.
Thanks for reaching out to us, will try to reproduce this issue.
I've noticed this bug as well when working on a prototype runBook for my team, which is effectively blocked without a fix for this - both on async and non-async variants of the ReplaceContentRunbookDraft
method. It definitely is the case that the stream is being disposed - based on my debugging it appears that it's between the initial call to _runbookDraftRestClient.ReplaceContentAsync
and the subsequent call to AutomationArmOperation
in Azure.ResourceManager.Automation.AutomationRunbookResource..ReplaceContentRunbookDraft
that the dispose occurs, but I'm not 100% sure if that's accurate or just how things appear to be.
This issue is reproduced and confirmed, opened #33907 to fix it.
Close this issue as the fix is merged, we will release a new version for automation soon.
Have released a new version with the fix of this problem: https://www.nuget.org/packages/Azure.ResourceManager.Automation/1.0.1
Confirmed this works only if you define a memory stream directly. It does still have to be seekable. In other words, you cannot generate the stream elsewhere, so the false positive is partly fixed and has a working method. It would still be good to be able to generate a stream from a string with a return or just a string instead of a stream. Is it possible to also do this while we are at it?
using MemoryStream stream = new();
using StreamWriter writer = new(stream);
writer.Write(code);
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
_ = await runbook.ReplaceContentRunbookDraftAsync(WaitUntil.Completed, stream);
//_ = await runbook.ReplaceContentRunbookDraftAsync(WaitUntil.Completed, Globals.GenerateStreamFromString(code));
It's correct that the stream that is being passed to ReplaceContentRunbookDraft has to be seekable. Here what I fixed is that we shouldn't get the exception stream must be seekable
with a seekable stream. And back to the question about changing the type of this parameter to string, our SDK is based on the API spec, so please open an issue here to let the service team know this need.