pnp / powershell

PnP PowerShell
https://pnp.github.io/powershell
MIT License
655 stars 337 forks source link

[BUG] Register-PnPManagementShellAccess: The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services. #24

Closed rachaelsingleton closed 3 years ago

rachaelsingleton commented 3 years ago

Reporting an Issue or Missing Feature

I am unable to run Register-PnPManagementShellAccess successfully.

Expected behavior

Registers without issue.

Actual behavior

Register-PnPManagementShellAccess: The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services.

Steps to reproduce behavior

I'm using PS 7 and VSCode.

What is the version of the Cmdlet module you are running?

Manifest 0.2.2 nightly PnP.PowerShell {Add-Alert, Add-App, Add-ApplicationCustomizer, Add-ClientSidePage…}

erwinvanhunen commented 3 years ago

Have you tried running this outside of the context of VS Code, e.g. in normal PowerShell session/Windows Terminal? We're investigating currently what is causing this as I cannot reproduce it in my install of VS Code. As it's a one time action only, per tenant, we might end up stating that VS Code is not supported (effectively what we are doing is a launching a browser that runs through a consent flow, and that someone seems to fail).

rachaelsingleton commented 3 years ago

It works in a normal PS Session, but not in VS code.

rachaelsingleton commented 3 years ago

From what I am reading it looks like it has to do with PowerShellEditorServices

Edit: I opened an issue with vs code - https://github.com/PowerShell/vscode-powershell/issues/3057 I've attached the logs here for your reference.

EditorServices.log

rjmholt commented 3 years ago

I'm not sure of the stack trace, but it might be due to this:

https://github.com/pnp/powershell/blob/8ad231c7b04472cd283b0ceee31d59e58211bd2e/src/Commands/Base/RegisterPnPManagementShellAccess.cs#L35

If cmdlet methods are being called off the pipeline thread, which might happen with an asynchronous execution like this (where the pipeline thread calls out to the task threadpool and then waits for the result), then we might see an error like this.

Certainly PSES does more thread juggling than the ordinary PowerShell host, so we tend to catch more issues when cmdlets play with threads; the PS console host's implementation details tend to mean threading issues don't become bugs.

rjmholt commented 3 years ago

We can know a lot more with the output of Get-Error

rachaelsingleton commented 3 years ago

Exception : Type : System.Management.Automation.PSInvalidOperationException ErrorRecord : Exception : Type : System.Management.Automation.ParentContainsErrorRecordException Message : The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services. HResult : -2146233087 CategoryInfo : InvalidOperation: (:) [], ParentContainsErrorRecordException FullyQualifiedErrorId : InvalidOperation TargetSite : Name : ProcessRecord DeclaringType : PnP.PowerShell.Commands.Base.ConnectOnline MemberType : Method Module : PnP.PowerShell.dll StackTrace : at PnP.PowerShell.Commands.Base.ConnectOnline.ProcessRecord() in D:\a\powershell\powershell\src\Commands\Base\ConnectOnline.cs:line 264 at System.Management.Automation.CommandProcessor.ProcessRecord() Message : The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services. Data : System.Collections.ListDictionaryInternal Source : PnP.PowerShell HResult : -2146233079 CategoryInfo : InvalidOperation: (:) [Connect-PnPOnline], PSInvalidOperationException FullyQualifiedErrorId : InvalidOperation,PnP.PowerShell.Commands.Base.ConnectOnline InvocationInfo : MyCommand : Connect-PnPPnPOnline ScriptLineNumber : 1 OffsetInLine : 1 HistoryId : 37 Line : Connect-PnPOnline -Url "~removed~" -PnPManagementShell -verbose PositionMessage : At line:1 char:1

rjmholt commented 3 years ago

@rachaelsingleton can you wrap the output in a triple backtick block to make it easier to read? In one of our repos I'd do that for you, but I don't have edit permission here.

rjmholt commented 3 years ago

I notice the line number in the stack trace doesn't line up with the current source, but there are no GitHub releases in this repo. @erwinvanhunen what's the easiest way to get the source corresponding to a particular version?

rachaelsingleton commented 3 years ago

    Type        : System.Management.Automation.PSInvalidOperationException
    ErrorRecord : 
        Exception             : 
            Type    : System.Management.Automation.ParentContainsErrorRecordException
            Message : The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and
they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services.
            HResult : -2146233087
        CategoryInfo          : InvalidOperation: (:) [], ParentContainsErrorRecordException
        FullyQualifiedErrorId : InvalidOperation
    TargetSite  : 
        Name          : ThrowIfWriteNotPermitted
        DeclaringType : System.Management.Automation.MshCommandRuntime, System.Management.Automation, Version=7.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
        MemberType    : Method
        Module        : System.Management.Automation.dll
    StackTrace  : 
   at System.Management.Automation.MshCommandRuntime.ThrowIfWriteNotPermitted(Boolean needsToWriteToPipeline)
   at System.Management.Automation.MshCommandRuntime.WriteHelper_ShouldWrite(ActionPreference preference, ContinueStatus lastContinueStatus)
   at System.Management.Automation.MshCommandRuntime.WriteWarning(WarningRecord record, Boolean overrideInquire)
   at System.Management.Automation.MshCommandRuntime.WriteWarning(String text)
   at System.Management.Automation.Cmdlet.WriteWarning(String text)
   at PnP.PowerShell.Commands.Utilities.AzureAuthHelper.WriteFormattedWarning(PSCmdlet cmdlet, String message) in
D:\a\powershell\powershell\src\Commands\Utilities\AzureAuthHelper.cs:line 54
   at PnP.PowerShell.Commands.Base.RegisterPnPManagementShellAccess.<ProcessRecord>b__2_0(DeviceCodeResult codeResult) in
D:\a\powershell\powershell\src\Commands\Base\RegisterPnPManagementShellAccess.cs:line 35
   at Microsoft.Identity.Client.Internal.Requests.DeviceCodeRequest.ExecuteAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenWithDeviceCodeParameters
deviceCodeParameters, CancellationToken cancellationToken)
   at PnP.PowerShell.Commands.Base.RegisterPnPManagementShellAccess.ProcessRecord() in D:\a\powershell\powershell\src\Commands\Base\RegisterPnPManagementShellAccess.cs:line 32
   at System.Management.Automation.CommandProcessor.ProcessRecord()
    Message     : The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they 
can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services.
    Source      : System.Management.Automation
    HResult     : -2146233079
CategoryInfo          : InvalidOperation: (:) [Register-PnPManagementShellAccess], PSInvalidOperationException
FullyQualifiedErrorId : InvalidOperation,PnP.PowerShell.Commands.Base.RegisterPnPManagementShellAccess
InvocationInfo        : 
    MyCommand        : Register-PnPPnPManagementShellAccess
    ScriptLineNumber : 1
    OffsetInLine     : 1
    HistoryId        : 7
    Line             : Register-PnPManagementShellAccess
    PositionMessage  : At line:1 char:1
                       + Register-PnPManagementShellAccess
                       + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    InvocationName   : Register-PnPManagementShellAccess
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1```
rjmholt commented 3 years ago

Yeah, this looks like my original hypothesis. See in the stack trace:

   at System.Management.Automation.Cmdlet.WriteWarning(String text)
   at PnP.PowerShell.Commands.Utilities.AzureAuthHelper.WriteFormattedWarning(PSCmdlet cmdlet, String message) in
D:\a\powershell\powershell\src\Commands\Utilities\AzureAuthHelper.cs:line 54
   at PnP.PowerShell.Commands.Base.RegisterPnPManagementShellAccess.<ProcessRecord>b__2_0(DeviceCodeResult codeResult) in
D:\a\powershell\powershell\src\Commands\Base\RegisterPnPManagementShellAccess.cs:line 35
   at Microsoft.Identity.Client.Internal.Requests.DeviceCodeRequest.ExecuteAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenWithDeviceCodeParameters
deviceCodeParameters, CancellationToken cancellationToken)
   at PnP.PowerShell.Commands.Base.RegisterPnPManagementShellAccess.ProcessRecord() in D:\a\powershell\powershell\src\Commands\Base\RegisterPnPManagementShellAccess.cs:line 32

My strong suspicion is that the async calls (which are proper async/await calls, looking through the source) are pushing the call onto a different thread, which ends up throwing. It's not safe to call back to cmdlet methods from async code without some guarantee that the callback will be done on the right thread.

Possible mitigations for this are:

BaronSparky commented 3 years ago

I am experiencing the same issue also.

Consistent in PS ISE for "Register-PnPManagementShellAccess" and "Connect-PnPOnline" but also see it in VS Code for just "Register-PnPManagementShellAccess"

erwinvanhunen commented 3 years ago

We just merged a commit (https://github.com/pnp/powershell/commit/604067c44b29298c8a7bf05d6fdf1fe497ba6691) that should resolve the issues mentioned in this thread. Please test version 0.3.9-nightly which will be available tomorrow in the PowerShell Gallery.

erwinvanhunen commented 3 years ago

@BaronSparky , @rachaelsingleton : would you be able to test it with the 0.3.9-nightly release in your environment? If the issues are gone with VSCode/ISE for you we can close this issues. Thanks in advance!

BaronSparky commented 3 years ago

@erwinvanhunen - I have checked with 0.3.9 just now on my environment and works just fine in the PowerShell ISE.

Thanks.

erwinvanhunen commented 3 years ago

@rachaelsingleton : As both ISE and VSCode had the same issue with PnP PowerShell outputting results on a different thread and this has been fixed and confirmed to work by @BaronSparky in PowerShell ISE I will close this issue. Do not hesitate to open a new issue if it still stands in your environment.