azure-functions-powershell-worker icon indicating copy to clipboard operation
azure-functions-powershell-worker copied to clipboard

Connect-EXOPSSession Unavailable in Azure Functions

Open luckerby opened this issue 4 years ago • 6 comments

Azure CloudShell has been offering for some time now the ability of using Connect-EXOPSSession, without importing any module manually.

However trying to use the Connect-EXOPSSession cmdlet within an Azure Function results in the error below. Is there anything extra required to have this working - along with other Exchange Online cmdlets - or it's simply not supported for now ?

2020-07-29T20:15:03Z   [Error]   ERROR: The term 'Connect-EXOPSSession' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

Exception             : 
    Type        : System.Management.Automation.CommandNotFoundException
    ErrorRecord : 
        Exception             : 
            Type    : System.Management.Automation.ParentContainsErrorRecordException
            Message : The term 'Connect-EXOPSSession' is not recognized as the name of a cmdlet, function, script file, or operable program.
                      Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
            HResult : -2146233087
        TargetObject          : Connect-EXOPSSession
        CategoryInfo          : ObjectNotFound: (Connect-EXOPSSession:String) [], ParentContainsErrorRecordException
        FullyQualifiedErrorId : CommandNotFoundException
        InvocationInfo        : 
            ScriptLineNumber : 12
            OffsetInLine     : 1
            HistoryId        : 1
            ScriptName       : D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage\run.ps1
            Line             : Connect-EXOPSSession
                               
            PositionMessage  : At D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage\run.ps1:12 char:1
                               + Connect-EXOPSSession
                               + ~~~~~~~~~~~~~~~~~~~~
            PSScriptRoot     : D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage
            PSCommandPath    : D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage\run.ps1
            InvocationName   : Connect-EXOPSSession
            CommandOrigin    : Internal
        ScriptStackTrace      : at <ScriptBlock>, D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage\run.ps1: line 12
    CommandName : Connect-EXOPSSession
    TargetSite  : 
        Name          : LookupCommandInfo
        DeclaringType : System.Management.Automation.CommandDiscovery, System.Management.Automation, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
        MemberType    : Method
        Module        : System.Management.Automation.dll
    StackTrace  : 
   at System.Management.Automation.CommandDiscovery.LookupCommandInfo(String commandName, CommandTypes commandTypes, SearchResolutionOptions searchResolutionOptions, CommandOrigin commandOrigin, ExecutionContext context)
   at System.Management.Automation.CommandDiscovery.LookupCommandProcessor(String commandName, CommandOrigin commandOrigin, Nullable`1 useLocalScope)
   at System.Management.Automation.ExecutionContext.CreateCommand(String command, Boolean dotSource)
   at System.Management.Automation.PipelineOps.AddCommand(PipelineProcessor pipe, CommandParameterInternal[] commandElements, CommandBaseAst commandBaseAst, CommandRedirection[] redirections, ExecutionContext context)
   at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
   at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Message     : The term 'Connect-EXOPSSession' is not recognized as the name of a cmdlet, function, script file, or operable program.
                  Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    Data        : System.Collections.ListDictionaryInternal
    Source      : System.Management.Automation
    HResult     : -2146233087
TargetObject          : Connect-EXOPSSession
CategoryInfo          : ObjectNotFound: (Connect-EXOPSSession:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
InvocationInfo        : 
    ScriptLineNumber : 12
    OffsetInLine     : 1
    HistoryId        : 1
    ScriptName       : D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage\run.ps1
    Line             : Connect-EXOPSSession
                       
    PositionMessage  : At D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage\run.ps1:12 char:1
                       + Connect-EXOPSSession
                       + ~~~~~~~~~~~~~~~~~~~~
    PSScriptRoot     : D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage
    PSCommandPath    : D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage\run.ps1
    InvocationName   : Connect-EXOPSSession
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, D:\home\site\wwwroot\RetrieveO365PhotosAndInjectToStorage\run.ps1: line 12

luckerby avatar Jul 29 '20 20:07 luckerby

HI @luckerby -- By default, the only cmdlets available in the Azure Functions Service is whatever ships with PowerShell Core 6.2 or 7 (depending on the version you are using in your function app). However, you can install additional modules for your function app using Managed Dependencies. For more information, please see https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-powershell#dependency-management.

Here is what the requirements.psd1 file will look like:



# This file enables modules to be automatically managed by the Functions service.
# See https://aka.ms/functionsmanageddependency for additional information.
#
@{
    'MSApiConnect'= '1.0.0' 
}

Please keep in mind that the module needs to be PowerShell Core compliant. If this is not the case, we can provide you with a potential work around. Let us know.

Cheers, Francisco

Francisco-Gamino avatar Aug 07 '20 04:08 Francisco-Gamino

Hi @Francisco-Gamino,

There's currently 2 versions of the Exchange Online modules. Each consists of a limited set of cmdlets. Upon using a Connect-* type of cmdlet (Connect-EXOPSSession for V1 and Connect-ExchangeOnline for V2) and successfully connecting, an implicit remoting module is downloaded, and the majority of cmdlets for operating against Exchange objects are made available.

For the Exchange Online V1 module (which Microsoft no longer recommends using in favor of V2), it's explicitly stated that "The Exchange Online Remote PowerShell Module is not supported in PowerShell Core (macOS, Linux, or Windows Nano Server)". You mentioned a workaround for this, but since V1 is not the preferred way anymore, let's look at the next version.

For V2 - which contains both 9 new cmdlets as well as the old ones in V1, the Connect-ExchangeOnline cmdlet resides in the ExchangeOnlineManagement module. The .psm1 for this module references 2 assemblies:

  • Microsoft.Exchange.Management.RestApiClient.dll (I believe new V2 cmdlets are in here)
  • Microsoft.Exchange.Management.ExoPowershellGalleryModule.dll (this probably contains the old V1 cmdlets) Using a decompiler (Jetbrains dotPeek) to check the target framework shows .Net Framework v4.7.2. So again there's no .NET Core targeting.

Can you let me know more details around the workaround ?

luckerby avatar Aug 07 '20 12:08 luckerby

One more thing that could affect us - trying to run V2 module's Connect-ExchangeOnline in a Cloud Shell eventually throws the error below.

New-ExoPSSession: /home/mihai/.local/share/powershell/Modules/ExchangeOnlineManagement/1.0.1/ExchangeOnlineManagement.psm1:449
Line |
 449 |  … PSSession = New-ExoPSSession -ExchangeEnvironmentName $ExchangeEnviro …
     |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The type initializer for 'Microsoft.Exchange.Management.AdminApiProvider.Authentication.UserTokenProvider' threw an exception.

luckerby avatar Aug 07 '20 12:08 luckerby

Hello @luckerby ,

Here is the potential workaround for the V2 module which might have some dependencies that are not Core CLR compliant.

  1. Install PowerShell Core 7 locally. Install module + dependencies (if any). Then, import the module in PowerShell 7 using the -UseWindowsPowerShell flag, e.g., Import-Module <moduleName> -UseWindowsPowerShell If the module imports with no errors, run the commands you need for your script. If everything works correctly, then it is possible that it will run in the Azure Functions service.

    To install PowerShell 7, go to https://github.com/PowerShell/PowerShell/releases/, or just run this in PowerShell: iex "& { $(irm 'https://aka.ms/install-powershell.ps1')} -UseMSI"

  2. If (1) did not work, try to reach out to the team that owns the module to see if there are any plans to make the module + dependencies Core CLR complaint.

  3. Alternatively, you could try to use Functions V1 + PowerShell experimental support (for Windows PowerShell 5.1). Please keep in mind that this is just experimental support and there are no plans to improve this offering.

Francisco-Gamino avatar Aug 07 '20 19:08 Francisco-Gamino

Another workaround is to spawn a powershell.exe process from the function: https://github.com/Azure/azure-functions-powershell-worker/issues/232#issuecomment-536744760.

AnatoliB avatar Sep 01 '20 18:09 AnatoliB

For those still experiencing this issue, this might do the trick

haneef95 avatar Aug 25 '21 21:08 haneef95