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

Parser.ParseFile should support specifying a session context #19825

Open
Francisco-Gamino opened this issue Jun 19, 2023 · 4 comments
Open

Parser.ParseFile should support specifying a session context #19825

Francisco-Gamino opened this issue Jun 19, 2023 · 4 comments
Labels
Needs-Triage The issue is new and needs to be triaged by a work group.

Comments

@Francisco-Gamino
Copy link
Contributor

Francisco-Gamino commented Jun 19, 2023

Steps to reproduce

Currently, when parsing PowerShell files, there is not way to pass a session context. Instead, PowerShell uses a global context. This means that we do no have a way to only specify modules which are only available to a function app. As a workaround, we can install the modules locally in one of the global paths of $Env:PSModulePath. However, this will not work when working on Azure and it is a blocker for the PowerShell Functions v2 programming model which uses PowerShell 7.4.

This is the PowerShell API that we use the parse the files: https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.language.parser.parsefile?view=powershellsdk-7.3.0

For this repro, I would like to use a custom enum defined in my CustomModule PowerShell module.

Custom module definition:

$code = @'
using System;
using System.Management.Automation;

namespace CustomModule
{
    // Define custom enum
    public enum FruitType
    {
        Apple,
        Banana,
        Orange,
        Grape,
        Mango
    }

    [Cmdlet(VerbsCommon.Get, "Fruit")]
    public class GetFruitCmdlet : Cmdlet
    {
        // Parameter to accept the FruitType enum value from PowerShell
        [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)]
        public FruitType Fruit { get; set; }

        // The ProcessRecord method will be called when the cmdlet is executed
        protected override void ProcessRecord()
        {
            // Output the selected fruit to the PowerShell pipeline
            WriteObject($"You selected {Fruit.ToString()}.");
        }
    }
}
'@

# From this code, I generate the following assembly:
Add-Type -TypeDefinition $code  -OutputAssembly CustomModule.dll

CustomModule.psd1 module manifest:

@{
# Script module or binary module file associated with this manifest.
RootModule = 'CustomModule.dll'

# Version number of this module.
ModuleVersion = '0.0.1'

# ID used to uniquely identify this module
GUID = 'a29f3d82-c4e7-4ac2-8e09-7eb5afeae0f9'

FunctionsToExport = @()

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @("Get-Fruit")

# Variables to export from this module
VariablesToExport = '*'

# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()

}

This custom module is placed at the functionAppRoot\Modules directory (this path gets appended to the $env:PSModulePath variable before invoking the function).

PowerShell function definition (functions-parser-repro.psm1):

using namespace System.Net
using module AzureFunctions.PowerShell.SDK
#using module CustomModule

$psversion = $PSVersionTable.PSVersion.ToString()
$fullPSVersion = $PSVersionTable | Out-String

function MyHttpTriggerParseRepro
{
    [Function()]
    param(
        [HttpTrigger()]
        $Request,
        $TriggerMetadata
    )

    $fruitName = [CustomModule.FruitType]::Apple

    $body = "Hello world! from PowerShell $psversion" + [System.Environment]::NewLine
    $body += "Full psversion: " + $fullPSVersion + [System.Environment]::NewLine
    $body += "Fruit name: " + $fruitName + [System.Environment]::NewLine

    [HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = $body
    } | Push-OutputBinding -Name Response
}
ScriptBlockAst? fileAst = Parser.ParseFile(powerShellFile.FullName, out _, out ParseError[] errors);

Note: The parsing issue is because the types defined in the custom module are not loaded prior to parsing the file in the runspace.

Follow up question: In order to use the using <ModuleName> statement, does the module need to be available in the global $env:PSModulePath path?

Would it be possible to get this fixed for PowerShell 7.4?

Expected behavior

Parser.ParseFile should take a session context with the modules available to the function app, so that the types are available.

Actual behavior

`Parser.ParseFile` fails because the types are not loaded / available in the runspace.

Error details

[2023-07-27T02:02:11.964Z] ERROR: Unable to find type [CustomModule.FruitType].
[2023-07-27T02:02:11.966Z]
[2023-07-27T02:02:11.967Z] Exception             :
[2023-07-27T02:02:11.968Z]     Type        : System.Management.Automation.RuntimeException
[2023-07-27T02:02:11.969Z]     ErrorRecord :
[2023-07-27T02:02:11.970Z]         Exception             :
[2023-07-27T02:02:11.971Z]             Type    : System.Management.Automation.ParentContainsErrorRecordException
[2023-07-27T02:02:11.971Z]             Message : Unable to find type [CustomModule.FruitType].
[2023-07-27T02:02:11.972Z]             HResult : -2146233087
[2023-07-27T02:02:11.973Z]         TargetObject          : CustomModule.FruitType
[2023-07-27T02:02:11.974Z]         CategoryInfo          : InvalidOperation: (CustomModule.FruitType:TypeName) [], ParentContainsErrorRecordException
[2023-07-27T02:02:11.975Z]         FullyQualifiedErrorId : TypeNotFound
[2023-07-27T02:02:11.975Z]         InvocationInfo        :
[2023-07-27T02:02:11.976Z]             ScriptLineNumber : 19
[2023-07-27T02:02:11.977Z]             OffsetInLine     : 18
[2023-07-27T02:02:11.978Z]             HistoryId        : 1
[2023-07-27T02:02:11.979Z]             ScriptName       : C:\MyFunctionApps\PSNPMBugBash\functions-parser-repro.psm1
[2023-07-27T02:02:11.980Z]             Line             : $fruitName = [CustomModule.FruitType]::Apple
[2023-07-27T02:02:11.981Z]
[2023-07-27T02:02:11.982Z]             Statement        : [CustomModule.FruitType]
[2023-07-27T02:02:11.983Z]             PositionMessage  : At C:\MyFunctionApps\PSNPMBugBash\functions-parser-repro.psm1:19 char:18
[2023-07-27T02:02:11.984Z]                                +     $fruitName = [CustomModule.FruitType]::Apple
[2023-07-27T02:02:11.984Z]                                +                  ~~~~~~~~~~~~~~~~~~~~~~~~
[2023-07-27T02:02:11.985Z]             PSScriptRoot     : C:\MyFunctionApps\PSNPMBugBash
[2023-07-27T02:02:11.986Z]             PSCommandPath    : C:\MyFunctionApps\PSNPMBugBash\functions-parser-repro.psm1
[2023-07-27T02:02:11.987Z]             CommandOrigin    : Internal
[2023-07-27T02:02:11.988Z]         ScriptStackTrace      : at MyHttpTriggerParseRepro, C:\MyFunctionApps\PSNPMBugBash\functions-parser-repro.psm1: line 19
[2023-07-27T02:02:11.989Z]     TargetSite  :
[2023-07-27T02:02:11.990Z]         Name          : ResolveTypeName
[2023-07-27T02:02:11.991Z]         DeclaringType : System.Management.Automation.TypeOps, System.Management.Automation, Version=7.4.0.3, Culture=neutral, PublicKeyToken=31bf3856ad364e35
[2023-07-27T02:02:11.992Z]         MemberType    : Method
[2023-07-27T02:02:11.992Z]         Module        : System.Management.Automation.dll
[2023-07-27T02:02:11.993Z]     Message     : Unable to find type [CustomModule.FruitType].
[2023-07-27T02:02:11.994Z]     Data        : System.Collections.ListDictionaryInternal
[2023-07-27T02:02:11.996Z]     Source      : System.Management.Automation
[2023-07-27T02:02:11.998Z]     HResult     : -2146233087
[2023-07-27T02:02:11.998Z]     StackTrace  :
[2023-07-27T02:02:11.999Z]    at System.Management.Automation.TypeOps.ResolveTypeName(ITypeName typeName, IScriptExtent errorPos)
[2023-07-27T02:02:12.000Z]    at System.Management.Automation.Interpreter.FuncCallInstruction`3.Run(InterpretedFrame frame)
[2023-07-27T02:02:12.001Z]    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
[2023-07-27T02:02:12.002Z] TargetObject          : CustomModule.FruitType
[2023-07-27T02:02:12.002Z] CategoryInfo          : InvalidOperation: (CustomModule.FruitType:TypeName) [], RuntimeException
[2023-07-27T02:02:12.003Z] FullyQualifiedErrorId : TypeNotFound
[2023-07-27T02:02:12.004Z] InvocationInfo        :
[2023-07-27T02:02:12.005Z]     ScriptLineNumber : 19
[2023-07-27T02:02:12.006Z]     OffsetInLine     : 18
[2023-07-27T02:02:12.006Z]     HistoryId        : 1
[2023-07-27T02:02:12.007Z]     ScriptName       : C:\MyFunctionApps\PSNPMBugBash\functions-parser-repro.psm1
[2023-07-27T02:02:12.008Z]     Line             : $fruitName = [CustomModule.FruitType]::Apple
[2023-07-27T02:02:12.009Z]
[2023-07-27T02:02:12.011Z]     Statement        : [CustomModule.FruitType]
[2023-07-27T02:02:12.012Z]     PositionMessage  : At C:\MyFunctionApps\PSNPMBugBash\functions-parser-repro.psm1:19 char:18
[2023-07-27T02:02:12.013Z]                        +     $fruitName = [CustomModule.FruitType]::Apple
[2023-07-27T02:02:12.014Z]                        +                  ~~~~~~~~~~~~~~~~~~~~~~~~
[2023-07-27T02:02:12.015Z]     PSScriptRoot     : C:\MyFunctionApps\PSNPMBugBash
[2023-07-27T02:02:12.016Z]     PSCommandPath    : C:\MyFunctionApps\PSNPMBugBash\functions-parser-repro.psm1
[2023-07-27T02:02:12.017Z]     CommandOrigin    : Internal
[2023-07-27T02:02:12.018Z] ScriptStackTrace      : at MyHttpTriggerParseRepro, C:\MyFunctionApps\PSNPMBugBash\functions-parser-repro.psm1: line 19
[2023-07-27T02:02:12.019Z]
[2023-07-27T02:02:12.020Z]
[2023-07-27T02:02:12.020Z] Result: ERROR: Unable to find type [CustomModule.FruitType].

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.0-preview.3
PSEdition                      Core
GitCommitId                    7.4.0-preview.3
OS                             Microsoft Windows 6.2.9200
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

@Francisco-Gamino Francisco-Gamino added the Needs-Triage The issue is new and needs to be triaged by a work group. label Jun 19, 2023
@Francisco-Gamino
Copy link
Contributor Author

@SeeminglyScience
Copy link
Collaborator

Could you double check the error message you're getting? The parser won't validate whether a command exists, that's done at execution time

@Francisco-Gamino
Copy link
Contributor Author

@daxian-dbw / @SeeminglyScience -- I have added a repro as well as the exception information coming from Parser.ParseFile.

@daxian-dbw
Copy link
Member

@Francisco-Gamino I cannot reproduce the issue within pwsh by following the repro steps.
Theoretically, the parsing of the psm1 file doesn't require a custom type to be resolved unless the type is used in a PowerShell class definition. Feel free to ping me to take a look at the issue together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs-Triage The issue is new and needs to be triaged by a work group.
Projects
None yet
Development

No branches or pull requests

3 participants