Skip to content

The Microsoft.Graph.Authentication module encloses module/script paths in single quote without considering the paths could contain single quote in it #3527

@daxian-dbw

Description

@daxian-dbw

Describe the bug

Reference issue: PowerShell/PowerShell#26739

When a user's perosnal module path is under a business OneDrive folder like C:\Users\UserName\OneDrive - Company's Name\Documents\PowerShell\Modules and have the Microsoft.Graph.Authentication module installed there, which is the default location when installing a module with Install-Module or Install-PSResouce, the Connect-MgGraph command will fail with the following errors:

PS C:\Users\UserName> Connect-MgGraph -Scopes Organization.Read.All, Directory.Read.All
$ParseException/   at System.Management.Automation.ScriptBlock.Create(Parser parser, String fileName, String fileContents)
   at System.Management.Automation.ScriptBlock.Create(ExecutionContext context, String script)
   at System.Management.Automation.CommandInvocationIntrinsics.InvokeScript(String script)
   at Microsoft.Graph.PowerShell.PSCmdletExtensions.RunScript[T](CommandInvocationIntrinsics cii, String script)
   at Microsoft.Graph.PowerShell.PSCmdletExtensions.RunScript[T](PSCmdlet cmdlet, String script)
   at Microsoft.Graph.PowerShell.Authentication.Utilities.Runtime.Cmdlets.GetScriptCmdlet.GetScriptCmdlets(String scriptFolder)
   at Microsoft.Graph.PowerShell.Authentication.Utilities.Runtime.Cmdlets.GetScriptCmdlet.ProcessRecord()
$IncompleteParseException/   at System.Management.Automation.Runspaces.PipelineBase.Invoke(IEnumerable input)
   at System.Management.Automation.PowerShell.Worker.ConstructPipelineAndDoWork(Runspace rs, Boolean performSyncInvoke)
   at System.Management.Automation.PowerShell.Worker.CreateRunspaceIfNeededAndDoWork(Runspace rsToUse, Boolean isSync)
   at System.Management.Automation.PowerShell.CoreInvokeHelper[TInput,TOutput](PSDataCollection`1 input, PSDataCollection`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.CoreInvoke[TInput,TOutput](PSDataCollection`1 input, PSDataCollection`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.Invoke[T]()
   at Microsoft.Graph.PowerShell.PSCmdletExtensions.RunScript[T](String script)
   at Microsoft.Graph.PowerShell.Authentication.Utilities.Runtime.Cmdlets.GetModuleCmdlet.GetModuleCmdlets(String modulePath)
   at Microsoft.Graph.PowerShell.Authentication.Utilities.Runtime.Cmdlets.GetModuleCmdlet.ProcessRecord()
Connect-MgGraph: The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

The root cause can be found in PowerShell/PowerShell#26739 (comment), which I quote here:

It's a bug in the the Microsoft.Graph.Authentication module. After decompiling Microsoft.Graph.Authentication.dll from the module, we can find the implementation of GetModuleCmdlet.GetModuleCmdlets(String modulePath) and GetScriptCmdlet.GetScriptCmdlets(String scriptFolder) as follows:

// Microsoft.Graph.PowerShell.Authentication.<...>.GetModuleCmdlet.GetModuleCmdlets(String modulePath)
private IEnumerable<CommandInfo> GetModuleCmdlets(string modulePath)
{
   return PSCmdletExtensions.RunScript<CommandInfo>("(Get-Command -Module (Import-Module '" + modulePath + "' -PassThru))");
}

// Microsoft.Graph.PowerShell.Authentication.<...>.GetScriptCmdlet.GetScriptCmdlets(String scriptFolder)
private IEnumerable<FunctionInfo> GetScriptCmdlets(string scriptFolder)
{
   string script = "\r\n                $currentFunctions = Get-ChildItem function:\r\n                Get-ChildItem -Path '" + scriptFolder + "' -Recurse -Include '*.ps1' -File | ForEach-Object { . $_.FullName }\r\n                Get-ChildItem function: | Where-Object { ($currentFunctions -notcontains $_) -and $_.CmdletBinding }\r\n                ";
   return ((PSCmdlet)(object)this).RunScript<FunctionInfo>(script);
}

When constructing the scripts to run with the paths, they simply enclose the paths with single quotes. In your case, when the module is installed in the personal module path (under one drive), the path already contains a single quote, and thus the scripts constructed in those above 2 methods will have incorrect syntax and cause the observed parser errors when Connect-MgGraph runs.

The Microsoft.Graph.Authentication module should use System.Management.Automation.Language.CodeGeneration.EscapeSingleQuotedStringContent to escape the potential single quote in the path before using it in the script.

Expected behavior

Connect-MgGraph should work.

How to reproduce

  1. Install the Microsoft.Graph module to a location that has a single quote in the path
  2. Import the module Microsoft.Graph.Authentication from that location
  3. Run Connect-MgGraph -Scopes Organization.Read.All, Directory.Read.All
  4. The errors described above will show up

SDK Version

Microsoft.Graph.Authentication module version 2.35.1

Latest version known to work for scenario above?

No response

Known Workarounds

Install the module to a path that has no single quote in it.

Debug output

Click to expand log ``` PowerShell 7.5.4 PS C:\Users\UserName> Install-Module Microsoft.Graph

Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you
want to install the modules from 'PSGallery'?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): y

PS C:\Users\UserName> Connect-MgGraph -Scopes Organization.Read.All, Directory.Read.All
$ParseException/ at System.Management.Automation.ScriptBlock.Create(Parser parser, String fileName, String fileContents)
at System.Management.Automation.ScriptBlock.Create(ExecutionContext context, String script)
at System.Management.Automation.CommandInvocationIntrinsics.InvokeScript(String script)
at Microsoft.Graph.PowerShell.PSCmdletExtensions.RunScript[T](CommandInvocationIntrinsics cii, String script)
at Microsoft.Graph.PowerShell.PSCmdletExtensions.RunScript[T](PSCmdlet cmdlet, String script)
at Microsoft.Graph.PowerShell.Authentication.Utilities.Runtime.Cmdlets.GetScriptCmdlet.GetScriptCmdlets(String scriptFolder)
at Microsoft.Graph.PowerShell.Authentication.Utilities.Runtime.Cmdlets.GetScriptCmdlet.ProcessRecord()
$IncompleteParseException/ at System.Management.Automation.Runspaces.PipelineBase.Invoke(IEnumerable input)
at System.Management.Automation.PowerShell.Worker.ConstructPipelineAndDoWork(Runspace rs, Boolean performSyncInvoke)
at System.Management.Automation.PowerShell.Worker.CreateRunspaceIfNeededAndDoWork(Runspace rsToUse, Boolean isSync)
at System.Management.Automation.PowerShell.CoreInvokeHelper[TInput,TOutput](PSDataCollection1 input, PSDataCollection1 output, PSInvocationSettings settings)
at System.Management.Automation.PowerShell.CoreInvoke[TInput,TOutput](PSDataCollection1 input, PSDataCollection1 output, PSInvocationSettings settings)
at System.Management.Automation.PowerShell.InvokeT
at Microsoft.Graph.PowerShell.PSCmdletExtensions.RunScript[T](String script)
at Microsoft.Graph.PowerShell.Authentication.Utilities.Runtime.Cmdlets.GetModuleCmdlet.GetModuleCmdlets(String modulePath)
at Microsoft.Graph.PowerShell.Authentication.Utilities.Runtime.Cmdlets.GetModuleCmdlet.ProcessRecord()
Connect-MgGraph: The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

PS C:\Users\UserName> Get-Error

Exception :
Type : System.Management.Automation.CommandNotFoundException
ErrorRecord :
Exception :
Type : System.Management.Automation.ParentContainsErrorRecordException
Message : The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable 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-MgGraph
CategoryInfo : ObjectNotFound: (Connect-MgGraph:String) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : CommandNotFoundException
InvocationInfo :
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : 1
Line : Connect-MgGraph -Scopes Organization.Read.All, Directory.Read.All
Statement : Connect-MgGraph
PositionMessage : At line:1 char:1
+ Connect-MgGraph -Scopes Organization.Read.All, Directory.Read.All
+ ~~~~~~~~~~~~~~~
InvocationName : Connect-MgGraph
CommandOrigin : Internal
ScriptStackTrace : at , : line 1
CommandName : Connect-MgGraph
TargetSite :
Name : LookupCommandInfo
DeclaringType : [System.Management.Automation.CommandDiscovery]
MemberType : Method
Module : System.Management.Automation.dll
Message : The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable 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
StackTrace :
at System.Management.Automation.CommandDiscovery.LookupCommandInfo(String commandName, CommandTypes commandTypes, SearchResolutionOptions searchResolutionOptions, CommandOrigin
commandOrigin, ExecutionContext context)
at System.Management.Automation.CommandDiscovery.TryModuleAutoDiscovery(String commandName, ExecutionContext context, String originalCommandName, CommandOrigin commandOrigin,
SearchResolutionOptions searchResolutionOptions, CommandTypes commandTypes, Exception& lastError)
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, Nullable1 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.ActionCallInstruction6.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject : Connect-MgGraph
CategoryInfo : ObjectNotFound: (Connect-MgGraph:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
InvocationInfo :
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : 1
Line : Connect-MgGraph -Scopes Organization.Read.All, Directory.Read.All
Statement : Connect-MgGraph
PositionMessage : At line:1 char:1
+ Connect-MgGraph -Scopes Organization.Read.All, Directory.Read.All
+ ~~~~~~~~~~~~~~~
InvocationName : Connect-MgGraph
CommandOrigin : Internal
ScriptStackTrace : at , : line 1

PS C:\Users\UserName> $env:PSModulePath -split ";"
C:\Users\UserName\OneDrive - Company's Name\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
PS C:\Users\UserName>

</details>


### Configuration

_No response_

### Other information

_No response_

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:waiting-for-triageAn issue that is yet to be reviewed or assignedtype:bugA broken experience

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions