17
17
namespace Microsoft . Azure . Functions . PowerShellWorker . PowerShell
18
18
{
19
19
using System . Management . Automation ;
20
+ using System . Reflection ;
20
21
21
22
internal class PowerShellManager
22
23
{
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.
26
-
27
- readonly static string s_LogAndSetReturnValueScript = @"
28
- param([Parameter(ValueFromPipeline=$true)]$return)
29
-
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
24
readonly static string s_TriggerMetadataParameterName = "TriggerMetadata" ;
25
+ readonly static bool s_UseLocalScope = true ;
43
26
44
27
RpcLogger _logger ;
45
28
PowerShell _pwsh ;
46
29
47
- PowerShellManager ( RpcLogger logger )
30
+ PowerShellManager ( PowerShell pwsh , RpcLogger logger )
48
31
{
49
- _pwsh = System . Management . Automation . PowerShell . Create ( InitialSessionState . CreateDefault ( ) ) ;
32
+ _pwsh = pwsh ;
50
33
_logger = logger ;
51
34
52
35
// Setup Stream event listeners
@@ -60,63 +43,33 @@ internal class PowerShellManager
60
43
}
61
44
62
45
public static PowerShellManager Create ( RpcLogger logger )
63
- {
64
- var manager = new PowerShellManager ( logger ) ;
65
-
66
- // Add HttpResponseContext namespace so users can reference
67
- // 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
46
{
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 )
47
+ // Set up initial session state: set execution policy, import helper module, and using namespace
48
+ var initialSessionState = InitialSessionState . CreateDefault ( ) ;
49
+ if ( Platform . IsWindows )
80
50
{
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 ) ;
51
+ initialSessionState . ExecutionPolicy = Microsoft . PowerShell . ExecutionPolicy . Unrestricted ;
94
52
}
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 ( ) ;
104
- }
105
-
106
- void ExecuteScriptAndClearCommands ( string script )
107
- {
108
- _pwsh . AddScript ( script ) . Invoke ( ) ;
109
- _pwsh . Commands . Clear ( ) ;
110
- }
53
+ var pwsh = PowerShell . Create ( initialSessionState ) ;
111
54
112
- public Collection < T > ExecuteScriptAndClearCommands < T > ( string script )
113
- {
114
- var result = _pwsh . AddScript ( script ) . Invoke < T > ( ) ;
115
- _pwsh . Commands . Clear ( ) ;
116
- return result ;
55
+ // Add HttpResponseContext namespace so users can reference
56
+ // HttpResponseContext without needing to specify the full namespace
57
+ // and also import the Azure Functions binding helper module
58
+ string modulePath = System . IO . Path . Join (
59
+ AppDomain . CurrentDomain . BaseDirectory ,
60
+ "Azure.Functions.PowerShell.Worker.Module" ,
61
+ "Azure.Functions.PowerShell.Worker.Module.psd1" ) ;
62
+ pwsh . AddScript ( $ "using namespace { typeof ( HttpResponseContext ) . Namespace } ")
63
+ . AddStatement ( )
64
+ . AddCommand ( "Import-Module" )
65
+ . AddParameter ( "Name" , modulePath )
66
+ . AddParameter ( "Scope" , "Global" )
67
+ . InvokeAndClearCommands ( ) ;
68
+
69
+ return new PowerShellManager ( pwsh , logger ) ;
117
70
}
118
71
119
- public PowerShellManager InvokeFunctionAndSetGlobalReturn (
72
+ public Hashtable InvokeFunction (
120
73
string scriptPath ,
121
74
string entryPoint ,
122
75
Hashtable triggerMetadata ,
@@ -135,15 +88,23 @@ public PowerShellManager InvokeFunctionAndSetGlobalReturn(
135
88
{
136
89
if ( entryPoint != "" )
137
90
{
138
- ExecuteScriptAndClearCommands ( $@ ". { scriptPath } ") ;
139
- parameterMetadata = ExecuteScriptAndClearCommands < FunctionInfo > ( $@ "Get-Command { entryPoint } ") [ 0 ] . Parameters ;
140
- _pwsh . AddScript ( $@ ". { entryPoint } @args") ;
91
+ parameterMetadata = _pwsh
92
+ . AddScript ( $@ ". { scriptPath } ", s_UseLocalScope )
93
+ . AddStatement ( )
94
+ . AddCommand ( "Get-Command" , s_UseLocalScope ) . AddParameter ( "Name" , entryPoint )
95
+ . InvokeAndClearCommands < FunctionInfo > ( ) [ 0 ] . Parameters ;
96
+
97
+ _pwsh
98
+ . AddScript ( $@ ". { scriptPath } ", s_UseLocalScope )
99
+ . AddStatement ( )
100
+ . AddCommand ( entryPoint , s_UseLocalScope ) ;
141
101
142
102
}
143
103
else
144
104
{
145
- parameterMetadata = ExecuteScriptAndClearCommands < ExternalScriptInfo > ( $@ "Get-Command { scriptPath } ") [ 0 ] . Parameters ;
146
- _pwsh . AddScript ( $@ ". { scriptPath } @args") ;
105
+ parameterMetadata = _pwsh . AddCommand ( "Get-Command" , s_UseLocalScope ) . AddParameter ( "Name" , scriptPath )
106
+ . InvokeAndClearCommands < ExternalScriptInfo > ( ) [ 0 ] . Parameters ;
107
+ _pwsh . AddCommand ( scriptPath , s_UseLocalScope ) ;
147
108
}
148
109
}
149
110
@@ -160,35 +121,47 @@ public PowerShellManager InvokeFunctionAndSetGlobalReturn(
160
121
_logger . LogDebug ( $ "TriggerMetadata found. Value:{ Environment . NewLine } { triggerMetadata . ToString ( ) } ") ;
161
122
}
162
123
163
- // This script handles when the user adds something to the pipeline.
124
+ PSObject returnObject = null ;
164
125
using ( ExecutionTimer . Start ( _logger , "Execution of the user's function completed." ) )
165
126
{
166
- ExecuteScriptAndClearCommands ( s_LogAndSetReturnValueScript ) ;
127
+ // Log everything we received from the pipeline and set the last one to be the ReturnObject
128
+ Collection < PSObject > pipelineItems = _pwsh . InvokeAndClearCommands < PSObject > ( ) ;
129
+ foreach ( var psobject in pipelineItems )
130
+ {
131
+ _logger . LogInformation ( psobject . ToString ( ) ) ;
132
+ }
133
+
134
+ returnObject = pipelineItems [ pipelineItems . Count - 1 ] ;
167
135
}
168
- return this ;
169
- }
170
- catch ( Exception e )
171
- {
172
- ResetRunspace ( ) ;
173
- throw e ;
174
- }
175
- }
136
+
137
+ var result = _pwsh . AddCommand ( "Get-OutputBinding" , s_UseLocalScope ) . InvokeAndClearCommands < Hashtable > ( ) [ 0 ] ;
176
138
177
- public Hashtable ReturnBindingHashtable ( IDictionary < string , BindingInfo > outBindings )
178
- {
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 ] ;
139
+ if ( returnObject != null )
140
+ {
141
+ result . Add ( "$return" , returnObject ) ;
142
+ }
184
143
ResetRunspace ( ) ;
185
144
return result ;
186
145
}
187
146
catch ( Exception e )
188
147
{
189
148
ResetRunspace ( ) ;
190
- throw e ;
149
+ throw ;
191
150
}
192
151
}
152
+
153
+ void ResetRunspace ( )
154
+ {
155
+ // Reset the runspace to the Initial Session State
156
+ _pwsh . Runspace . ResetRunspaceState ( ) ;
157
+
158
+ // TODO: Change this to clearing the variable by running in the module
159
+ string modulePath = System . IO . Path . Join ( AppDomain . CurrentDomain . BaseDirectory , "Azure.Functions.PowerShell.Worker.Module" , "Azure.Functions.PowerShell.Worker.Module.psd1" ) ;
160
+ _pwsh . AddCommand ( "Import-Module" )
161
+ . AddParameter ( "Name" , modulePath )
162
+ . AddParameter ( "Scope" , "Global" )
163
+ . AddParameter ( "Force" )
164
+ . InvokeAndClearCommands ( ) ;
165
+ }
193
166
}
194
167
}
0 commit comments