77using System . Collections ;
88using System . Collections . Generic ;
99using System . Collections . ObjectModel ;
10- using System . Text ;
10+ using System . IO ;
1111
1212using Microsoft . Azure . Functions . PowerShellWorker . Utility ;
1313using Microsoft . Azure . WebJobs . Script . Grpc . Messages ;
@@ -20,33 +20,16 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell
2020
2121 internal class PowerShellManager
2222 {
23- // This script handles when the user adds something to the pipeline.
24- // It logs the item that comes and stores it as the $return out binding.
25- // The last item stored as $return will be returned to the function host.
23+ private const string _TriggerMetadataParameterName = "TriggerMetadata" ;
2624
27- readonly static string s_LogAndSetReturnValueScript = @"
28- param([Parameter(ValueFromPipeline=$true)]$return)
25+ private RpcLogger _logger ;
26+ private PowerShell _pwsh ;
2927
30- Write-Information $return
31-
32- Set-Variable -Name '$return' -Value $return -Scope global
33- " ;
34-
35- readonly static string s_SetExecutionPolicyOnWindowsScript = @"
36- if ($IsWindows)
37- {
38- Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process
39- }
40- " ;
41-
42- readonly static string s_TriggerMetadataParameterName = "TriggerMetadata" ;
43-
44- RpcLogger _logger ;
45- PowerShell _pwsh ;
46-
47- PowerShellManager ( RpcLogger logger )
28+ internal PowerShellManager ( RpcLogger logger )
4829 {
49- _pwsh = PowerShell . Create ( InitialSessionState . CreateDefault ( ) ) ;
30+ var initialSessionState = InitialSessionState . CreateDefault ( ) ;
31+ initialSessionState . ExecutionPolicy = Microsoft . PowerShell . ExecutionPolicy . Unrestricted ;
32+ _pwsh = PowerShell . Create ( initialSessionState ) ;
5033 _logger = logger ;
5134
5235 // Setup Stream event listeners
@@ -59,64 +42,17 @@ internal class PowerShellManager
5942 _pwsh . Streams . Warning . DataAdding += streamHandler . WarningDataAdding ;
6043 }
6144
62- public static PowerShellManager Create ( RpcLogger logger )
63- {
64- var manager = new PowerShellManager ( logger ) ;
65-
45+ internal void InitializeRunspace ( )
46+ {
6647 // Add HttpResponseContext namespace so users can reference
6748 // HttpResponseContext without needing to specify the full namespace
68- manager . ExecuteScriptAndClearCommands ( $ "using namespace { typeof ( HttpResponseContext ) . Namespace } ") ;
69- manager . ExecuteScriptAndClearCommands ( s_SetExecutionPolicyOnWindowsScript ) ;
70- return manager ;
71- }
72-
73- static string BuildBindingHashtableScript ( IDictionary < string , BindingInfo > outBindings )
74- {
75- // Since all of the out bindings are stored in variables at this point,
76- // we must construct a script that will return those output bindings in a hashtable
77- StringBuilder script = new StringBuilder ( ) ;
78- script . AppendLine ( "@{" ) ;
79- foreach ( KeyValuePair < string , BindingInfo > binding in outBindings )
80- {
81- script . Append ( "'" ) ;
82- script . Append ( binding . Key ) ;
83-
84- // since $return has a dollar sign, we have to treat it differently
85- if ( binding . Key == "$return" )
86- {
87- script . Append ( "' = " ) ;
88- }
89- else
90- {
91- script . Append ( "' = $" ) ;
92- }
93- script . AppendLine ( binding . Key ) ;
94- }
95- script . AppendLine ( "}" ) ;
96-
97- return script . ToString ( ) ;
98- }
99-
100- void ResetRunspace ( )
101- {
102- // Reset the runspace to the Initial Session State
103- _pwsh . Runspace . ResetRunspaceState ( ) ;
49+ _pwsh . AddScript ( $ "using namespace { typeof ( HttpResponseContext ) . Namespace } ") . InvokeAndClearCommands ( ) ;
50+
51+ // Set the PSModulePath
52+ Environment . SetEnvironmentVariable ( "PSModulePath" , Path . Join ( AppDomain . CurrentDomain . BaseDirectory , "Modules" ) ) ;
10453 }
10554
106- void ExecuteScriptAndClearCommands ( string script )
107- {
108- _pwsh . AddScript ( script ) . Invoke ( ) ;
109- _pwsh . Commands . Clear ( ) ;
110- }
111-
112- public Collection < T > ExecuteScriptAndClearCommands < T > ( string script )
113- {
114- var result = _pwsh . AddScript ( script ) . Invoke < T > ( ) ;
115- _pwsh . Commands . Clear ( ) ;
116- return result ;
117- }
118-
119- public PowerShellManager InvokeFunctionAndSetGlobalReturn (
55+ internal Hashtable InvokeFunction (
12056 string scriptPath ,
12157 string entryPoint ,
12258 Hashtable triggerMetadata ,
@@ -130,20 +66,25 @@ public PowerShellManager InvokeFunctionAndSetGlobalReturn(
13066 // If it does, we invoke the command of that name. We also need to fetch
13167 // the ParameterMetadata so that we can tell whether or not the user is asking
13268 // for the $TriggerMetadata
133-
13469 using ( ExecutionTimer . Start ( _logger , "Parameter metadata retrieved." ) )
13570 {
13671 if ( entryPoint != "" )
13772 {
138- ExecuteScriptAndClearCommands ( $@ ". { scriptPath } ") ;
139- parameterMetadata = ExecuteScriptAndClearCommands < FunctionInfo > ( $@ "Get-Command { entryPoint } ") [ 0 ] . Parameters ;
140- _pwsh . AddScript ( $@ ". { entryPoint } @args") ;
73+ parameterMetadata = _pwsh
74+ . AddCommand ( "Microsoft.PowerShell.Core\\ Import-Module" ) . AddParameter ( "Name" , scriptPath )
75+ . AddStatement ( )
76+ . AddCommand ( "Microsoft.PowerShell.Core\\ Get-Command" ) . AddParameter ( "Name" , entryPoint )
77+ . InvokeAndClearCommands < FunctionInfo > ( ) [ 0 ] . Parameters ;
78+
79+ _pwsh . AddCommand ( entryPoint ) ;
14180
14281 }
14382 else
14483 {
145- parameterMetadata = ExecuteScriptAndClearCommands < ExternalScriptInfo > ( $@ "Get-Command { scriptPath } ") [ 0 ] . Parameters ;
146- _pwsh . AddScript ( $@ ". { scriptPath } @args") ;
84+ parameterMetadata = _pwsh . AddCommand ( "Microsoft.PowerShell.Core\\ Get-Command" ) . AddParameter ( "Name" , scriptPath )
85+ . InvokeAndClearCommands < ExternalScriptInfo > ( ) [ 0 ] . Parameters ;
86+
87+ _pwsh . AddCommand ( scriptPath ) ;
14788 }
14889 }
14990
@@ -154,41 +95,52 @@ public PowerShellManager InvokeFunctionAndSetGlobalReturn(
15495 }
15596
15697 // Gives access to additional Trigger Metadata if the user specifies TriggerMetadata
157- if ( parameterMetadata . ContainsKey ( s_TriggerMetadataParameterName ) )
98+ if ( parameterMetadata . ContainsKey ( _TriggerMetadataParameterName ) )
15899 {
159- _pwsh . AddParameter ( s_TriggerMetadataParameterName , triggerMetadata ) ;
100+ _pwsh . AddParameter ( _TriggerMetadataParameterName , triggerMetadata ) ;
160101 _logger . LogDebug ( $ "TriggerMetadata found. Value:{ Environment . NewLine } { triggerMetadata . ToString ( ) } ") ;
161102 }
162103
163- // This script handles when the user adds something to the pipeline.
104+ PSObject returnObject = null ;
164105 using ( ExecutionTimer . Start ( _logger , "Execution of the user's function completed." ) )
165106 {
166- ExecuteScriptAndClearCommands ( s_LogAndSetReturnValueScript ) ;
107+ // Log everything we received from the pipeline and set the last one to be the ReturnObject
108+ Collection < PSObject > pipelineItems = _pwsh . InvokeAndClearCommands < PSObject > ( ) ;
109+ foreach ( var psobject in pipelineItems )
110+ {
111+ _logger . LogInformation ( $ "OUTPUT: { psobject . ToString ( ) } ") ;
112+ }
113+
114+ returnObject = pipelineItems [ pipelineItems . Count - 1 ] ;
115+ }
116+
117+ var result = _pwsh . AddCommand ( "Azure.Functions.PowerShell.Worker.Module\\ Get-OutputBinding" )
118+ . AddParameter ( "Purge" )
119+ . InvokeAndClearCommands < Hashtable > ( ) [ 0 ] ;
120+
121+ if ( returnObject != null )
122+ {
123+ result . Add ( "$return" , returnObject ) ;
167124 }
168- return this ;
125+ return result ;
169126 }
170- catch ( Exception e )
127+ finally
171128 {
172- ResetRunspace ( ) ;
173- throw e ;
129+ ResetRunspace ( scriptPath ) ;
174130 }
175131 }
176132
177- public Hashtable ReturnBindingHashtable ( IDictionary < string , BindingInfo > outBindings )
133+ private void ResetRunspace ( string scriptPath )
178134 {
179- try
180- {
181- // This script returns a hashtable that contains the
182- // output bindings that we will return to the function host.
183- var result = ExecuteScriptAndClearCommands < Hashtable > ( BuildBindingHashtableScript ( outBindings ) ) [ 0 ] ;
184- ResetRunspace ( ) ;
185- return result ;
186- }
187- catch ( Exception e )
188- {
189- ResetRunspace ( ) ;
190- throw e ;
191- }
135+ // Reset the runspace to the Initial Session State
136+ _pwsh . Runspace . ResetRunspaceState ( ) ;
137+
138+ // If the function had an entry point, this will remove the module that was loaded
139+ var moduleName = Path . GetFileNameWithoutExtension ( scriptPath ) ;
140+ _pwsh . AddCommand ( "Microsoft.PowerShell.Core\\ Remove-Module" )
141+ . AddParameter ( "Name" , moduleName )
142+ . AddParameter ( "ErrorAction" , "SilentlyContinue" )
143+ . InvokeAndClearCommands ( ) ;
192144 }
193145 }
194146}
0 commit comments